package cmd

import (
	"fmt"
	"os"

	"git.wntrmute.dev/kyle/arca/internal/config"
	"git.wntrmute.dev/kyle/arca/internal/cryptsetup"
	"git.wntrmute.dev/kyle/arca/internal/udisks"
	"git.wntrmute.dev/kyle/arca/internal/unlock"
	"github.com/spf13/cobra"
	"golang.org/x/term"
)

var mountCmd = &cobra.Command{
	Use:   "mount <device|alias>",
	Short: "Unlock and mount a LUKS volume",
	Args:  cobra.ExactArgs(1),
	RunE:  runMount,
}

func init() {
	rootCmd.AddCommand(mountCmd)
}

func runMount(cmd *cobra.Command, args []string) error {
	cfg := config.Load()
	target := args[0]

	client, err := udisks.NewClient()
	if err != nil {
		return fmt.Errorf("connecting to udisks2: %w", err)
	}
	defer client.Close()

	devCfg := cfg.ResolveDevice(target)

	dev, err := client.FindDevice(devCfg.UUID, target)
	if err != nil {
		return err
	}

	// Check if already unlocked.
	if cleartext, err := client.CleartextDevice(dev); err == nil {
		// Already unlocked — check if mounted too.
		if mp, mounted := client.IsMounted(cleartext); mounted {
			fmt.Println(mp)
			return nil
		}
		// Unlocked but not mounted — just mount it.
		return doMount(client, cleartext, devCfg)
	}

	// Need to unlock.
	u := unlock.New(client, unlock.Options{
		ReadPassphrase: readPassphrase,
		KeyfilePath:    devCfg.Keyfile,
	})

	result, err := u.Unlock(dev, devCfg.Methods)
	if err != nil {
		return err
	}

	if result.Privileged {
		mountpoint, err := cryptsetup.Mount(result.Device.DevicePath, devCfg.Mountpoint)
		if err != nil {
			return fmt.Errorf("mounting: %w", err)
		}
		fmt.Println(mountpoint)
		return nil
	}

	return doMount(client, result.Device, devCfg)
}

func doMount(client *udisks.Client, cleartext *udisks.BlockDevice, devCfg config.ResolvedDevice) error {
	mountpoint, err := client.Mount(cleartext)
	if err != nil {
		return fmt.Errorf("mounting: %w", err)
	}
	fmt.Println(mountpoint)
	return nil
}

func readPassphrase() (string, error) {
	fmt.Fprint(os.Stderr, "Passphrase: ")
	pass, err := term.ReadPassword(int(os.Stdin.Fd()))
	fmt.Fprintln(os.Stderr)
	if err != nil {
		return "", fmt.Errorf("reading passphrase: %w", err)
	}
	return string(pass), nil
}
