package main

import (
	"errors"
	"fmt"
	"net/mail"
	"regexp"
	"strings"

	"git.tilde.town/tildetown/town/invites"
	"git.tilde.town/tildetown/town/sshkey"
	"git.tilde.town/tildetown/town/stats"
	"github.com/AlecAivazis/survey/v2"
	"github.com/charmbracelet/lipgloss"
)

func surveyIconSet(icons *survey.IconSet) {
	icons.Question.Text = "~"
	icons.Question.Format = "magenta:b"
}

func promptCode() (code string, err error) {
	err = survey.AskOne(&survey.Input{
		Message: "invite code?",
	}, &code,
		survey.WithValidator(survey.Required),
		survey.WithIcons(surveyIconSet))
	code = strings.TrimSpace(code)
	return
}

func confirmCoC() (conf bool, err error) {
	err = survey.AskOne(
		&survey.Confirm{
			Message: "do you agree to the above CoC?",
			Default: true,
		}, &conf,
		survey.WithValidator(survey.Required),
		survey.WithIcons(surveyIconSet))
	return

}

func confirmContinue() (conf bool, err error) {
	err = survey.AskOne(
		&survey.Confirm{
			Message: "Does the above look ok?",
		}, &conf,
		survey.WithValidator(survey.Required),
		survey.WithIcons(surveyIconSet))
	return
}

type asker struct {
	UserData *newUserData
	Style    lipgloss.Style
	Invite   invites.Invite
	TownData stats.TildeData
}

func (a *asker) Ask() (err error) {
	if err = a.promptUsername(); err != nil {
		return err
	}

	if err = a.promptEmail(); err != nil {
		return err
	}

	if err = a.promptKey(); err != nil {
		return err
	}

	s := a.Style.SetString(
		fmt.Sprintf(`ok! your account is about to be created with the following details:

username: %s
email: %s
pubkey: %s`, a.UserData.Username, a.UserData.Email, a.UserData.PubKey)).Bold(true).MaxWidth(80)

	fmt.Println(s)

	return nil
}

func (a *asker) promptUsername() (err error) {
	// copied from /etc/adduser.conf
	usernameRE := regexp.MustCompile(`^[a-z][-a-z0-9_]*$`)
	err = survey.AskOne(
		&survey.Input{
			Message: "desired username?",
			Default: a.UserData.Username,
		}, &a.UserData.Username,
		survey.WithValidator(survey.Required),
		survey.WithIcons(surveyIconSet),
		survey.WithValidator(func(val interface{}) error {
			un := val.(string)
			if len(un) > 32 {
				return fmt.Errorf("username '%s' is too long", un)
			}
			return nil
		}),
		survey.WithValidator(func(val interface{}) error {
			un := val.(string)
			if !usernameRE.MatchString(un) {
				return errors.New("usernames must start with a letter and only contain letters, nubers, - or _")
			}
			return nil
		}),
		survey.WithValidator(func(val interface{}) error {
			un := val.(string)
			for _, v := range a.TownData.Users {
				if v.Username == un {
					return fmt.Errorf("username '%s' is already in use", un)
				}
			}
			return nil
		}))

	return
}

func (a *asker) promptEmail() (err error) {
	err = survey.AskOne(
		&survey.Input{
			Message: "e-mail (for account recovery only)?",
			Default: a.UserData.Email,
		}, &a.UserData.Email,
		survey.WithValidator(survey.Required),
		survey.WithIcons(surveyIconSet),
		survey.WithValidator(func(val interface{}) error {
			email := val.(string)
			_, err := mail.ParseAddress(email)
			if err != nil {
				return fmt.Errorf("'%s' doesn't look like an email: %w", email, err)
			}

			if !strings.Contains(email, ".") {
				return fmt.Errorf("'%s' doesn't look like an email: domain not fully qualified", email)
			}

			return nil
		}))

	return
}

func (a *asker) promptKey() (err error) {
	err = survey.AskOne(
		&survey.Input{
			Message: "SSH public key?",
			Default: a.UserData.PubKey,
		}, &a.UserData.PubKey,
		survey.WithValidator(survey.Required),
		survey.WithIcons(surveyIconSet),
		survey.WithValidator(func(v interface{}) error {
			key := v.(string)
			valid, err := sshkey.ValidKey(key)
			if err != nil {
				return fmt.Errorf("failed to validate key: %w", err)
			}
			if !valid {
				return errors.New("that doesn't seem like a valid SSH key. try another public key?")
			}
			return nil
		}))

	return
}
