package device

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/curusarn/resh/internal/futil"
	"github.com/curusarn/resh/internal/output"
	"github.com/google/uuid"
	isatty "github.com/mattn/go-isatty"
)

const fnameID = "device-id"
const fnameName = "device-name"

const fpathIDLegacy = ".resh/resh-uuid"

const filePerm = 0644

// Getters

func GetID(dataDir string) (string, error) {
	return readValue(dataDir, fnameID)
}

func GetName(dataDir string) (string, error) {
	return readValue(dataDir, fnameName)
}

// Install helpers

func SetupID(dataDir string) error {
	return setIDIfUnset(dataDir)
}

func SetupName(out *output.Output, dataDir string) error {
	return promptAndWriteNameIfUnset(out, dataDir)
}

func readValue(dataDir, fname string) (string, error) {
	fpath := path.Join(dataDir, fname)
	dat, err := os.ReadFile(fpath)
	if err != nil {
		return "", fmt.Errorf("could not read file with %s: %w", fname, err)
	}
	val := strings.TrimRight(string(dat), "\n")
	return val, nil
}

func setIDIfUnset(dataDir string) error {
	fpath := path.Join(dataDir, fnameID)
	exists, err := futil.FileExists(fpath)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}

	// Try copy device ID from legacy location
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return fmt.Errorf("could not get user home: %w", err)
	}
	fpathLegacy := path.Join(homeDir, fpathIDLegacy)
	exists, err = futil.FileExists(fpath)
	if err != nil {
		return err
	}
	if exists {
		futil.CopyFile(fpathLegacy, fpath)
		if err != nil {
			return fmt.Errorf("could not copy device ID from legacy location: %w", err)
		}
		return nil
	}

	// Generate new device ID
	rnd, err := uuid.NewRandom()
	if err != nil {
		return fmt.Errorf("could not get new random source: %w", err)
	}
	id := rnd.String()
	if id == "" {
		return fmt.Errorf("got invalid UUID from package")
	}
	err = os.WriteFile(fpath, []byte(id), filePerm)
	if err != nil {
		return fmt.Errorf("could not write generated ID to file: %w", err)
	}
	return nil
}

func promptAndWriteNameIfUnset(out *output.Output, dataDir string) error {
	fpath := path.Join(dataDir, fnameName)
	exists, err := futil.FileExists(fpath)
	if err != nil {
		return err
	}
	if exists {
		return nil
	}

	name, err := promptForName(out, fpath)
	if err != nil {
		return fmt.Errorf("error while prompting for input: %w", err)
	}
	err = os.WriteFile(fpath, []byte(name), filePerm)
	if err != nil {
		return fmt.Errorf("could not write name to file: %w", err)
	}
	return nil
}

func promptForName(out *output.Output, fpath string) (string, error) {
	// This function should be only ran from install-utils with attached terminal
	if !isatty.IsTerminal(os.Stdout.Fd()) {
		return "", fmt.Errorf("output is not a terminal - write name of this device to '%s' to bypass this error", fpath)
	}
	host, err := os.Hostname()
	if err != nil {
		return "", fmt.Errorf("could not get hostname (prompt default): %w", err)
	}
	hostStub := strings.Split(host, ".")[0]
	fmt.Printf("\nPlease choose a short name for this device (default: '%s'): ", hostStub)
	var input string
	n, err := fmt.Scanln(&input)
	if n != 1 {
		return "", fmt.Errorf("expected 1 value from prompt got %d", n)
	}
	if err != nil {
		return "", fmt.Errorf("scanln error: %w", err)
	}
	out.Info(fmt.Sprintf("Device name set to '%s'", input))
	fmt.Printf("You can change the device name at any time by editing '%s' file\n", fpath)
	return input, nil
}
