package main

import (
	"embed"
	"fmt"
	"math/rand"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strings"

	"git.tilde.town/tildetown/town/email"
	"github.com/charmbracelet/glamour"
	"github.com/charmbracelet/huh"
	"gopkg.in/yaml.v3"
)

//go:embed banners/*
var banners embed.FS

//go:embed md/*
var md embed.FS

func banner() (string, error) {
	dirs, err := banners.ReadDir("banners")
	if err != nil {
		return "", err
	}
	mx := len(dirs)
	ix := rand.Intn(mx)
	content, err := banners.ReadFile(path.Join("banners", dirs[ix].Name()))
	if err != nil {
		return "", err
	}

	return string(content), nil
}

func info() error {
	infoContent, err := md.ReadFile("md/info.md")
	if err != nil {
		return err
	}
	out, err := glamour.Render(string(infoContent), "auto")
	if err != nil {
		return err
	}
	cmd := exec.Command("/usr/bin/bat")
	cmd.Stdin = strings.NewReader(out)
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

func schedule() error {
	scheduleContent, err := md.ReadFile("md/schedule.md")
	if err != nil {
		return err
	}
	r, _ := glamour.NewTermRenderer(
		// detect background color and pick either the default dark or light theme
		glamour.WithAutoStyle(),
		// wrap output at specific width (default is 80)
		glamour.WithWordWrap(120),
	)
	out, err := r.Render(string(scheduleContent))
	if err != nil {
		return err
	}
	cmd := exec.Command("/usr/bin/bat")
	cmd.Stdin = strings.NewReader(out)
	cmd.Stdout = os.Stdout
	return cmd.Run()
}

type physicalData struct {
	Arriving string
	Lodging  bool
	Cats     bool
}

type rsvpData struct {
	Username     string
	Being        string
	PhysicalData *physicalData `yaml:"PhysicalData,omitempty"`
	Freeform     string
}

func sendRSVPEmail(rd rsvpData) error {
	bs, err := yaml.Marshal(rd)
	if err != nil {
		return err
	}

	return email.SendLocalEmail("vilmibm", "RSVP TOWNCON25", string(bs))
}

func rsvp(o opts) error {
	var being string
	var arriving string
	lodging := true
	var freeform string
	cats := false

	form := huh.NewForm(
		huh.NewGroup(
			huh.NewSelect[string]().Title("How will you be attending?").
				Options(
					huh.NewOption("Digitally", "digital"),
					huh.NewOption("Phyiscally", "physical"),
				).Value(&being)),

		// Physical attendee form
		huh.NewGroup(
			huh.NewInput().Title("What day/time do you think you might arrive?").
				Value(&arriving),
		).WithHideFunc(func() bool {
			return being != "physical"
		}),
		huh.NewGroup(
			huh.NewConfirm().Title("Will you be staying overnight at vil's house?").
				Value(&lodging),
		).WithHideFunc(func() bool {
			return being != "physical"
		}),
		// cats
		huh.NewGroup(
			huh.NewConfirm().Title("are you allergic to or otherwise upset by cats?").
				Value(&cats),
		).WithHideFunc(func() bool {
			return being != "physical"
		}),
		// Catch all freeform
		huh.NewGroup(
			huh.NewText().
				Title("Anything you want me to know? Any questions?").
				Value(&freeform))).WithTheme(huh.ThemeBase())

	err := form.Run()
	if err != nil {
		return err
	}

	var pd *physicalData

	if being == "physical" {
		pd = &physicalData{
			Arriving: arriving,
			Lodging:  lodging,
			Cats:     cats,
		}
	}

	rd := rsvpData{
		Username:     o.Username,
		Being:        being,
		Freeform:     freeform,
		PhysicalData: pd}

	if err = sendRSVPEmail(rd); err != nil {
		return err
	}

	fmt.Println("THANK YOUUUU!U!U!UU!U!!!!~~~!~!~~!!!!!UUU")

	return nil
}

type submitData struct {
	Username    string
	Live        bool
	When        string
	Desc        string
	Length      string
	Interactive bool
	Freeform    string
}

func sendSubmitEmail(sd submitData) error {
	bs, err := yaml.Marshal(sd)
	if err != nil {
		return err
	}

	return email.SendLocalEmail("vilmibm", "SUBMISSION TOWNCON25", string(bs))
}

func submit(o opts) error {
	submitContent, err := md.ReadFile("md/submit.md")
	if err != nil {
		return err
	}
	out, err := glamour.Render(string(submitContent), "auto")
	if err != nil {
		return err
	}
	fmt.Println(out)

	submitting := true
	var live bool
	var talkWhen string
	var length string
	var desc string
	var freeform string
	var interactive bool

	notSubmitting := func() bool {
		return !submitting
	}

	form := huh.NewForm(
		huh.NewGroup(
			huh.NewConfirm().Title("So...wanna submit a thing?").Value(&submitting),
		),
		huh.NewGroup(
			huh.NewConfirm().Title("Excellent. Is it a live talk?").Value(&live),
		).WithHideFunc(notSubmitting),
		huh.NewGroup(
			huh.NewInput().Title("During what time frame can you give the talk").
				Description("please include relevant time zone information!").Value(&talkWhen),
		).WithHideFunc(func() bool { return !live || !submitting }),
		huh.NewGroup(
			huh.NewText().Title("Please describe your submission").
				Description("Include details like media format, topic, whether it's text or browser based.").Value(&desc),
		).WithHideFunc(notSubmitting),
		huh.NewGroup(
			huh.NewInput().TitleFunc(func() string {
				if live {
					return "About how long is your talk?"
				} else {
					return "If your piece has a duration (eg audio/video), approximately what is it?"
				}
			}, &live).Value(&length),
		).WithHideFunc(notSubmitting),
		huh.NewGroup(
			huh.NewConfirm().Title("Is your submission interactive?").
				Description("for example a game or website").
				Value(&interactive),
		).WithHideFunc(func() bool { return !submitting || live }),
		huh.NewGroup(
			huh.NewText().Title("Anything else I should know?").Value(&freeform),
		).WithHideFunc(notSubmitting),
	).WithTheme(huh.ThemeBase())

	if err := form.Run(); err != nil {
		return err
	}

	if !submitting {
		fmt.Println("ok! just run the command again if you change your mind~")
		return nil
	}

	sd := submitData{
		Username:    o.Username,
		Live:        live,
		When:        talkWhen,
		Length:      length,
		Desc:        desc,
		Freeform:    freeform,
		Interactive: interactive,
	}

	if err = sendSubmitEmail(sd); err != nil {
		return err
	}

	fmt.Println("THANK YOU!! I'll be in touch~")

	return nil
}

type euroData struct {
	Username   string
	Interested bool
}

func euro(o opts) error {
	euroContent, err := md.ReadFile("md/euro.md")
	if err != nil {
		return err
	}
	out, err := glamour.Render(string(euroContent), "auto")
	if err != nil {
		return err
	}
	fmt.Println(out)

	var interested bool

	form := huh.NewForm(
		huh.NewGroup(
			huh.NewConfirm().Title("So...would you be interested?").Value(&interested),
		)).WithTheme(huh.ThemeBase())

	if err := form.Run(); err != nil {
		return err
	}

	ed := euroData{
		Username:   o.Username,
		Interested: interested,
	}

	if err = sendEuroEmail(ed); err != nil {
		return err
	}

	fmt.Println("THANK YOU~!!")

	return nil
}

func sendEuroEmail(ed euroData) error {
	bs, err := yaml.Marshal(ed)
	if err != nil {
		return err
	}

	return email.SendLocalEmail("vilmibm", "TOWNCON25 EURO INTEREST", string(bs))
}

type voteData struct {
	Username string
	Days     []string
}

func vote(o opts) error {
	voteContent, err := md.ReadFile("md/vote.md")
	if err != nil {
		return err
	}
	out, err := glamour.Render(string(voteContent), "auto")
	if err != nil {
		return err
	}
	fmt.Println(out)

	var selections []string

	form := huh.NewForm(
		huh.NewGroup(
			huh.NewMultiSelect[string]().Title("which days work for you?").
				Options(
					huh.NewOption("18th", "18th"),
					huh.NewOption("25th", "25th"),
				).Value(&selections))).WithTheme(huh.ThemeBase())

	if err := form.Run(); err != nil {
		return err
	}

	vd := voteData{
		Username: o.Username,
		Days:     selections,
	}

	if err = sendVoteEmail(vd); err != nil {
		return err
	}

	fmt.Println("THANK YOU~!!")

	return nil
}

func sendVoteEmail(vd voteData) error {
	bs, err := yaml.Marshal(vd)
	if err != nil {
		return err
	}

	return email.SendLocalEmail("vilmibm", "TOWNCON25 DAY VOTE", string(bs))
}

type jamData struct {
	Username string
	Desc     string
	Physical bool
}

func jam(o opts) error {
	jamContent, err := md.ReadFile("md/jam.md")
	if err != nil {
		return err
	}
	out, err := glamour.Render(string(jamContent), "auto")
	if err != nil {
		return err
	}
	fmt.Println(out)

	submitting := true
	var desc string
	var physical bool

	notSubmitting := func() bool {
		return !submitting
	}

	form := huh.NewForm(
		huh.NewGroup(
			huh.NewConfirm().Title("So...wanna propose a jam?").Value(&submitting),
		),
		huh.NewGroup(
			huh.NewConfirm().Title("Excellent. Does it require being in person?").Value(&physical),
		).WithHideFunc(notSubmitting),
		huh.NewGroup(
			huh.NewText().Title("Describe the jam.").
				Description("Include details like required knowledge or tools").Value(&desc),
		).WithHideFunc(notSubmitting),
	).WithTheme(huh.ThemeBase())

	if err := form.Run(); err != nil {
		return err
	}

	if !submitting {
		fmt.Println("ok! just run the command again if you change your mind~")
		return nil
	}

	jd := jamData{
		Username: o.Username,
		Desc:     desc,
		Physical: physical,
	}

	if err = sendJamEmail(jd); err != nil {
		return err
	}

	fmt.Println("THANK YOU!! I'll be in touch~")

	return nil
}

func sendJamEmail(jd jamData) error {
	bs, err := yaml.Marshal(jd)
	if err != nil {
		return err
	}

	return email.SendLocalEmail("vilmibm", "JAMPROP TOWNCON25", string(bs))
}

type opts struct {
	Username string
}

func _main() error {
	u, err := user.Current()
	if err != nil {
		return err
	}

	o := opts{
		Username: u.Username,
	}

	b, err := banner()
	if err != nil {
		return err
	}

	fmt.Println(b)

	fmt.Print(fmt.Sprintf("\t\t^_^_^_^_^_^_^ hi ~%s ^_^_^_^_^_^_^\n", o.Username))
	fmt.Println()

	var mode string

	// NB: this could be a standalone prompt but when it is, ctrl+c behavior is goofed up.
	form := huh.NewForm(
		huh.NewGroup(
			huh.NewSelect[string]().Title("whadduyu wanna doo?").
				Options(
					//huh.NewOption("VOTE ON DAY", "vote"),
					//huh.NewOption("EUROPE: REGISTER INTEREST", "euro"),
					huh.NewOption("Get an info dump", "info"),
					//huh.NewOption("SEE SCHEDULE", "schedule"),
					huh.NewOption("RSVP", "rsvp"),
					huh.NewOption("Submit or propose a work (talk, art, etc)", "submit"),
					huh.NewOption("Propose a creative jam", "jam"),
					huh.NewOption("Quit", "quit"),
				).Value(&mode))).WithTheme(huh.ThemeBase())

	err = form.Run()
	if err != nil {
		return err
	}

	switch mode {
	case "info":
		return info()
	case "schedule":
		return schedule()
	case "rsvp":
		return rsvp(o)
	case "submit":
		return submit(o)
	case "jam":
		return jam(o)
	case "vote":
		return vote(o)
	case "euro":
		return euro(o)
	case "quit":
	default:
		return fmt.Errorf("wtf '%s'", mode)
	}

	return nil
}

func main() {
	if err := _main(); err != nil {
		fmt.Fprintf(os.Stderr, ":( failed: %s\n", err.Error())
		os.Exit(1)
	}
}
