package cli

import (
	"errors"
	"strconv"
	"strings"
	"time"

	"github.com/charmbracelet/bubbles/textarea"
	"github.com/charmbracelet/bubbles/textinput"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/plutov/ultrafocus/hosts"
)

type TickMsg time.Time

type sessionState uint

func doTick() tea.Cmd {
	return tea.Tick(time.Second, func(t time.Time) tea.Msg {
		return TickMsg(t)
	})
}

const (
	menuView sessionState = iota
	blacklistView
	timerView
)

type model struct {
	textarea              textarea.Model
	textinput             textinput.Model
	fatalErr              error
	status                hosts.FocusStatus
	domains               []string
	commandsListSelection int
	minutesLeft           int
	state                 sessionState
}

func NewModel() model {
	domains, status, err := hosts.ExtractDomainsFromHostsFile()

	if len(domains) == 0 {
		domains = hosts.DefaultDomains
	}

	return model{
		textarea:  GetTextareaModel(),
		textinput: GetInputModel(),
		domains:   domains,
		state:     menuView,
		status:    status,
		fatalErr:  err,
	}
}

func (m model) Init() tea.Cmd {
	if m.fatalErr != nil {
		return tea.Quit
	}

	return doTick()
}

func (m *model) getCommandsList() []command {
	if m.status == hosts.FocusStatusOn {
		return []command{commandFocusOff, commandConfigureBlacklist}
	}

	return []command{commandFocusOn, commandFocusOnWithTimer, commandConfigureBlacklist}
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmds []tea.Cmd
	var cmd tea.Cmd

	m.textarea, cmd = m.textarea.Update(msg)
	cmds = append(cmds, cmd)

	m.textinput, cmd = m.textinput.Update(msg)
	cmds = append(cmds, cmd)

	switch msg := msg.(type) {

	case TickMsg:
		if m.status == hosts.FocusStatusOn && m.minutesLeft > 0 {
			m.minutesLeft--
			if m.minutesLeft == 0 {
				m = commandFocusOff.Run(m)
			}
		}

		return m, doTick()
	case tea.KeyMsg:
		commands := m.getCommandsList()
		switch msg.String() {

		case "up", "k":
			if m.state == menuView && m.commandsListSelection > 0 {
				m.commandsListSelection--
			}

		case "down", "j":
			if m.state == menuView && m.commandsListSelection < len(commands)-1 {
				m.commandsListSelection++
			}

		case "enter", " ":
			if m.state == menuView {
				m = commands[m.commandsListSelection].Run(m)
				if m.fatalErr != nil {
					return m, tea.Quit
				}
			}
		case "ctrl+c", "q":
			return m, tea.Quit
		case "esc":
			if m.state == blacklistView {
				domains := strings.Split(m.textarea.Value(), "\n")
				domains = hosts.CleanDomainsList(domains)

				if err := hosts.WriteDomainsToHostsFile(domains, m.status); err != nil {
					m.fatalErr = err
					return m, tea.Quit
				}

				m.commandsListSelection = 0
				m.domains = domains
				m.state = menuView
				m.textarea.Blur()
			}
			if m.state == timerView {
				minutesStr := m.textinput.Value()
				minutes, err := strconv.Atoi(minutesStr)
				if err != nil || minutes <= 0 {
					m.fatalErr = errors.New("Invalid number of minutes")
					return m, tea.Quit
				}

				m = commandFocusOn.Run(m)

				m.minutesLeft = minutes
				m.commandsListSelection = 0
				m.state = menuView
				m.textinput.Blur()
			}
		}
	}

	return m, tea.Batch(cmds...)
}