package ui

import (
	"fmt"
	"strconv"
	"strings"
	"woke/db"

	tea "github.com/charmbracelet/bubbletea"
)

type cfgField int

const (
	cfgSnoozeMinutes cfgField = iota
	cfgTimeoutMinutes
	cfgDefaultSound
	cfgBlinkOnMs
	cfgBlinkOffMs
	cfgColorClock
	cfgColorAlarm
	cfgShowSeconds
	cfgServerURL
	cfgServerPassword
	cfgAPIPassword
	cfgPollSeconds
	cfgCrescendoEnabled
	cfgCrescendoStartPct
	cfgCrescendoEndPct
	cfgCrescendoDurationS
	cfgCallbackScript
	cfgFieldCount
)

type configModel struct {
	fields [cfgFieldCount]string
	active cfgField
	err    string
}

func newConfigModel(cfg db.Settings) *configModel {
	c := &configModel{}
	c.fields[cfgSnoozeMinutes] = strconv.Itoa(cfg.SnoozeMinutes)
	c.fields[cfgTimeoutMinutes] = strconv.Itoa(cfg.TimeoutMinutes)
	c.fields[cfgDefaultSound] = cfg.DefaultSound
	c.fields[cfgBlinkOnMs] = strconv.Itoa(cfg.BlinkOnMs)
	c.fields[cfgBlinkOffMs] = strconv.Itoa(cfg.BlinkOffMs)
	c.fields[cfgColorClock] = cfg.ColorClock
	c.fields[cfgColorAlarm] = cfg.ColorAlarm
	if cfg.ShowSeconds {
		c.fields[cfgShowSeconds] = "true"
	} else {
		c.fields[cfgShowSeconds] = "false"
	}
	c.fields[cfgServerURL] = cfg.ServerURL
	c.fields[cfgServerPassword] = cfg.ServerPassword
	c.fields[cfgAPIPassword] = cfg.APIPassword
	c.fields[cfgPollSeconds] = strconv.Itoa(cfg.PollSeconds)
	if cfg.CrescendoEnabled {
		c.fields[cfgCrescendoEnabled] = "true"
	} else {
		c.fields[cfgCrescendoEnabled] = "false"
	}
	c.fields[cfgCrescendoStartPct] = strconv.Itoa(cfg.CrescendoStartPct)
	c.fields[cfgCrescendoEndPct] = strconv.Itoa(cfg.CrescendoEndPct)
	c.fields[cfgCrescendoDurationS] = strconv.Itoa(cfg.CrescendoDurationS)
	c.fields[cfgCallbackScript] = cfg.CallbackScript
	return c
}

func (c *configModel) HandleKey(msg tea.KeyMsg, m *Model) (tea.Model, tea.Cmd) {
	key := msg.String()

	switch key {
	case "esc", "escape":
		m.mode = viewMain
		m.config = nil
		m.statusMsg = ""
		return *m, nil

	case "tab", "down":
		c.active = (c.active + 1) % cfgFieldCount
		return *m, nil

	case "shift+tab", "up":
		c.active = (c.active - 1 + cfgFieldCount) % cfgFieldCount
		return *m, nil

	case "enter":
		return c.save(m)

	case "backspace":
		field := &c.fields[c.active]
		if len(*field) > 0 {
			*field = (*field)[:len(*field)-1]
		}
		c.err = ""
		return *m, nil

	case "ctrl+u":
		c.fields[c.active] = ""
		c.err = ""
		return *m, nil

	case " ":
		// Toggle boolean fields
		if c.active == cfgShowSeconds || c.active == cfgCrescendoEnabled {
			field := &c.fields[c.active]
			if *field == "true" {
				*field = "false"
			} else {
				*field = "true"
			}
			return *m, nil
		}
		// Space as literal for other fields
		c.fields[c.active] += " "
		c.err = ""
		return *m, nil

	default:
		if len(key) == 1 {
			// Don't allow free typing on boolean fields
			if c.active == cfgShowSeconds || c.active == cfgCrescendoEnabled {
				return *m, nil
			}
			c.fields[c.active] += key
			c.err = ""
		}
		return *m, nil
	}
}

func (c *configModel) save(m *Model) (tea.Model, tea.Cmd) {
	snooze, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgSnoozeMinutes]))
	if err != nil || snooze < 1 || snooze > 60 {
		c.err = "Snooze must be 1-60 minutes"
		return *m, nil
	}

	timeout, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgTimeoutMinutes]))
	if err != nil || timeout < 1 || timeout > 60 {
		c.err = "Timeout must be 1-60 minutes"
		return *m, nil
	}

	blinkOn, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgBlinkOnMs]))
	if err != nil || blinkOn < 100 || blinkOn > 5000 {
		c.err = "Blink on must be 100-5000 ms"
		return *m, nil
	}

	blinkOff, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgBlinkOffMs]))
	if err != nil || blinkOff < 100 || blinkOff > 5000 {
		c.err = "Blink off must be 100-5000 ms"
		return *m, nil
	}

	colorClock := strings.TrimSpace(c.fields[cfgColorClock])
	if !isValidHexColor(colorClock) {
		c.err = "Clock color must be hex like #00FF88"
		return *m, nil
	}

	colorAlarm := strings.TrimSpace(c.fields[cfgColorAlarm])
	if !isValidHexColor(colorAlarm) {
		c.err = "Alarm color must be hex like #FF4444"
		return *m, nil
	}

	pollSeconds, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgPollSeconds]))
	if err != nil || pollSeconds < 1 || pollSeconds > 60 {
		c.err = "Poll seconds must be 1-60"
		return *m, nil
	}

	crescStartPct, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgCrescendoStartPct]))
	if err != nil || crescStartPct < 0 || crescStartPct > 100 {
		c.err = "Crescendo start must be 0-100%"
		return *m, nil
	}

	crescEndPct, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgCrescendoEndPct]))
	if err != nil || crescEndPct < 0 || crescEndPct > 150 {
		c.err = "Crescendo end must be 0-150%"
		return *m, nil
	}

	crescDuration, err := strconv.Atoi(strings.TrimSpace(c.fields[cfgCrescendoDurationS]))
	if err != nil || crescDuration < 1 || crescDuration > 300 {
		c.err = "Crescendo duration must be 1-300 seconds"
		return *m, nil
	}

	cfg := db.Settings{
		SnoozeMinutes:  snooze,
		TimeoutMinutes: timeout,
		DefaultSound:   strings.TrimSpace(c.fields[cfgDefaultSound]),
		BlinkOnMs:      blinkOn,
		BlinkOffMs:     blinkOff,
		ColorClock:     colorClock,
		ColorAlarm:     colorAlarm,
	}

	showSec := strings.TrimSpace(c.fields[cfgShowSeconds])
	cfg.ShowSeconds = showSec == "true"

	cfg.ServerURL = strings.TrimSpace(c.fields[cfgServerURL])
	cfg.ServerPassword = strings.TrimSpace(c.fields[cfgServerPassword])
	cfg.APIPassword = strings.TrimSpace(c.fields[cfgAPIPassword])
	cfg.PollSeconds = pollSeconds

	cfg.CrescendoEnabled = strings.TrimSpace(c.fields[cfgCrescendoEnabled]) == "true"
	cfg.CrescendoStartPct = crescStartPct
	cfg.CrescendoEndPct = crescEndPct
	cfg.CrescendoDurationS = crescDuration
	cfg.CallbackScript = strings.TrimSpace(c.fields[cfgCallbackScript])

	if cfg.DefaultSound == "" {
		cfg.DefaultSound = "default"
	}

	if err := m.store.SaveSettings(cfg); err != nil {
		c.err = fmt.Sprintf("Save failed: %v", err)
		return *m, nil
	}

	m.settings = cfg
	m.applySettings()
	m.mode = viewMain
	m.config = nil
	m.statusMsg = "Settings saved"
	return *m, nil
}

func (c *configModel) View() string {
	labels := [cfgFieldCount]string{
		"Snooze (min):",
		"Timeout (min):",
		"Default sound:",
		"Blink on (ms):",
		"Blink off (ms):",
		"Clock color:",
		"Alarm color:",
		"Show seconds:",
		"Server URL:",
		"Server pass:",
		"API password:",
		"Poll (sec):",
		"Enabled:",
		"Start %:",
		"End %:",
		"Duration (s):",
		"Script path:",
	}

	hints := [cfgFieldCount]string{
		"1-60",
		"1-60, auto-dismiss firing alarm",
		"'default' or path to sound file",
		"100-5000",
		"100-5000",
		"hex e.g. #00FF88",
		"hex e.g. #FF4444",
		"space to toggle",
		"empty=local, or http://host:9119",
		"password to auth with server",
		"password for incoming API requests",
		"1-60, client poll frequency",
		"space to toggle",
		"0-100, starting volume",
		"0-150, ending volume (>100 = overdrive)",
		"1-300, ramp duration in seconds",
		"called with: start|dismiss|snooze|timeout <name>",
	}

	// Section headers: field index -> section name
	sections := map[cfgField]string{
		cfgSnoozeMinutes:    "─── Alarm ───",
		cfgBlinkOnMs:        "─── Display ───",
		cfgServerURL:        "─── Network ───",
		cfgCrescendoEnabled: "─── Crescendo (pactl) ───",
		cfgCallbackScript:   "─── Hooks ───",
	}

	var lines []string
	lines = append(lines, TitleStyle.Render("Settings"), "")

	for i := cfgField(0); i < cfgFieldCount; i++ {
		// Insert section header if this field starts a new section
		if section, ok := sections[i]; ok {
			lines = append(lines, "", DividerStyle.Render(section))
		}

		labelStr := fmt.Sprintf("%15s", labels[i])
		value := c.fields[i]

		var style = FormLabelStyle
		if i == c.active {
			style = FormActiveStyle
			value += "█"
		}

		if len(value) > formValueWidth {
			value = value[len(value)-formValueWidth:]
		}
		valueStr := fmt.Sprintf("%-*s", formValueWidth, value)
		hintStr := fmt.Sprintf("%-*s", formHintWidth, hints[i])

		line := FormLabelStyle.Render(labelStr) + " " + style.Render(valueStr) + " " + HelpStyle.Render(hintStr)
		lines = append(lines, line)
	}

	if c.err != "" {
		lines = append(lines, "", FormErrorStyle.Render("  Error: "+c.err))
	}

	return strings.Join(lines, "\n")
}

func isValidHexColor(s string) bool {
	if len(s) != 7 || s[0] != '#' {
		return false
	}
	for _, c := range s[1:] {
		if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
			return false
		}
	}
	return true
}
