package cmd

import (
	"errors"
	"tinyauth/internal/utils"

	"github.com/charmbracelet/huh"
	"github.com/pquerna/otp/totp"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/spf13/cobra"
	"golang.org/x/crypto/bcrypt"
)

type verifyUserCmd struct {
	root *cobra.Command
	cmd  *cobra.Command

	interactive bool
	username    string
	password    string
	totp        string
	user        string
}

func newVerifyUserCmd(root *cobra.Command) *verifyUserCmd {
	return &verifyUserCmd{
		root: root,
	}
}

func (c *verifyUserCmd) Register() {
	c.cmd = &cobra.Command{
		Use:   "verify",
		Short: "Verify a user is set up correctly",
		Long:  `Verify a user is set up correctly meaning that it has a correct username, password and TOTP code.`,
		Run:   c.run,
	}

	c.cmd.Flags().BoolVarP(&c.interactive, "interactive", "i", false, "Validate a user interactively")
	c.cmd.Flags().StringVar(&c.username, "username", "", "Username")
	c.cmd.Flags().StringVar(&c.password, "password", "", "Password")
	c.cmd.Flags().StringVar(&c.totp, "totp", "", "TOTP code")
	c.cmd.Flags().StringVar(&c.user, "user", "", "Hash (username:hash:totp)")

	if c.root != nil {
		c.root.AddCommand(c.cmd)
	}
}

func (c *verifyUserCmd) GetCmd() *cobra.Command {
	return c.cmd
}

func (c *verifyUserCmd) run(cmd *cobra.Command, args []string) {
	log.Logger = log.Level(zerolog.InfoLevel)

	if c.interactive {
		form := huh.NewForm(
			huh.NewGroup(
				huh.NewInput().Title("User (username:hash:totp)").Value(&c.user).Validate((func(s string) error {
					if s == "" {
						return errors.New("user cannot be empty")
					}
					return nil
				})),
				huh.NewInput().Title("Username").Value(&c.username).Validate((func(s string) error {
					if s == "" {
						return errors.New("username cannot be empty")
					}
					return nil
				})),
				huh.NewInput().Title("Password").Value(&c.password).Validate((func(s string) error {
					if s == "" {
						return errors.New("password cannot be empty")
					}
					return nil
				})),
				huh.NewInput().Title("TOTP Code (optional)").Value(&c.totp),
			),
		)
		var baseTheme *huh.Theme = huh.ThemeBase()
		err := form.WithTheme(baseTheme).Run()
		if err != nil {
			log.Fatal().Err(err).Msg("Form failed")
		}
	}

	user, err := utils.ParseUser(c.user)
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to parse user")
	}

	if user.Username != c.username {
		log.Fatal().Msg("Username is incorrect")
	}

	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(c.password))
	if err != nil {
		log.Fatal().Msg("Password is incorrect")
	}

	if user.TotpSecret == "" {
		if c.totp != "" {
			log.Warn().Msg("User does not have TOTP secret")
		}
		log.Info().Msg("User verified")
		return
	}

	ok := totp.Validate(c.totp, user.TotpSecret)
	if !ok {
		log.Fatal().Msg("TOTP code incorrect")

	}

	log.Info().Msg("User verified")
}
