package tui

import "fmt"

// IntentKind represents a side effect the shell must perform.
type IntentKind string

const (
	IntentLoadAgents IntentKind = "load_agents"
	IntentLoadLogs   IntentKind = "load_logs"
	IntentTick       IntentKind = "tick"
	IntentQuit       IntentKind = "quit"

	// Agent-level
	IntentEnableAgent  IntentKind = "enable_agent"
	IntentDisableAgent IntentKind = "disable_agent"
	IntentReloadAgent  IntentKind = "reload_agent"  // hot-reload via SIGHUP (solo este agente)
	IntentReloadAll    IntentKind = "reload_all"    // hot-reload via SIGHUP (todos los agentes)
	IntentRestartAgent IntentKind = "restart_agent" // restart completo del launcher

	// Unified launcher operations
	IntentStartLauncher   IntentKind = "start_launcher"
	IntentStopLauncher    IntentKind = "stop_launcher"
	IntentRestartLauncher IntentKind = "restart_launcher"
	IntentKillLauncher    IntentKind = "kill_launcher"
	IntentRebuildRestart  IntentKind = "rebuild_restart"
	IntentRunTests        IntentKind = "run_tests"
	IntentRunGoTests      IntentKind = "run_go_tests"
	IntentRunE2ETests     IntentKind = "run_e2e_tests"
	IntentRunE2EHeadTests IntentKind = "run_e2e_head_tests"
	IntentRunAllTests     IntentKind = "run_all_tests"
)

// Intent is pure data describing a side effect to execute.
type Intent struct {
	Kind    IntentKind
	AgentID string
}

// KeyMsg is the pure representation of a key press.
// The bridge layer converts tea.KeyMsg into this.
type KeyMsg struct {
	Str string // "up", "down", "enter", "0", "q", "r", etc.
}

// WindowSizeMsg carries terminal dimensions.
type WindowSizeMsg struct {
	Width  int
	Height int
}

// Update is PURE: (Model, msg) → (Model, []Intent). No side effects.
func Update(model Model, msg interface{}) (Model, []Intent) {
	switch m := msg.(type) {

	case WindowSizeMsg:
		model.WindowWidth = m.Width
		model.WindowHeight = m.Height
		return model, nil

	case MsgAgentsLoaded:
		model.Agents = m.Agents
		model.LauncherRunning = m.LauncherRunning
		model.LauncherPID = m.LauncherPID
		model.LauncherUptime = m.LauncherUptime
		model.LauncherMemory = m.LauncherMemory
		model.LauncherCPU = m.LauncherCPU
		model.LauncherLogSize = m.LauncherLogSize
		if model.Screen == ScreenAgentList {
			if model.Cursor >= len(model.Agents) && len(model.Agents) > 0 {
				model.Cursor = len(model.Agents) - 1
			}
		}
		return model, []Intent{{Kind: IntentTick}}

	case MsgActionDone:
		if m.Err != nil {
			model.StatusMsg = fmt.Sprintf("Error: %s %s: %v", m.Action, m.AgentID, m.Err)
		} else if m.Action == "Reload" {
			model.StatusMsg = fmt.Sprintf("Reload OK — %s recargado sin interrupciones", m.AgentID)
		} else if m.Action == "Restart" {
			model.StatusMsg = "Restart OK — launcher reiniciado"
		} else {
			model.StatusMsg = fmt.Sprintf("%s %s OK — restart launcher to apply", m.Action, m.AgentID)
		}
		return model, []Intent{{Kind: IntentLoadAgents}}

	case MsgServerActionDone:
		if m.Err != nil {
			model.StatusMsg = fmt.Sprintf("Error: %s: %v", m.Action, m.Err)
		} else if m.Action == "Reload All" {
			model.StatusMsg = "Reload All OK — SIGHUP enviado al launcher"
		} else {
			model.StatusMsg = fmt.Sprintf("%s OK", m.Action)
		}
		return model, []Intent{{Kind: IntentLoadAgents}}

	case MsgRebuildDone:
		if !m.BuildOK {
			model.StatusMsg = fmt.Sprintf("Build failed: %s", m.BuildLog)
		} else if m.Err != nil {
			model.StatusMsg = fmt.Sprintf("Built OK, start failed: %v", m.Err)
		} else if m.Started {
			model.StatusMsg = "Built OK, launcher started"
		} else {
			model.StatusMsg = "Built OK"
		}
		return model, []Intent{{Kind: IntentLoadAgents}}

	case MsgLogsLoaded:
		model.LogLines = m.Lines
		model.LogScroll = max(0, len(m.Lines)-visibleLogLines(model))
		return model, nil

	case MsgTestsDone:
		model.Screen = ScreenTestOutput
		model.LogLines = m.Output
		model.LogScroll = 0
		model.Cursor = 0
		model.LastTestKind = m.Kind
		label := testKindLabel(m.Kind)
		if m.Passed {
			model.StatusMsg = label + " PASSED"
		} else {
			model.StatusMsg = label + " FAILED"
		}
		return model, nil

	case MsgTick:
		return model, []Intent{{Kind: IntentLoadAgents}}

	case KeyMsg:
		return updateKey(model, m)
	}

	return model, nil
}

func updateKey(model Model, key KeyMsg) (Model, []Intent) {
	if key.Str == "q" && model.Screen == ScreenMain {
		return model, []Intent{{Kind: IntentQuit}}
	}
	if key.Str == "ctrl+c" {
		return model, []Intent{{Kind: IntentQuit}}
	}

	switch model.Screen {
	case ScreenMain:
		return updateMainScreen(model, key)
	case ScreenAgentList:
		return updateAgentList(model, key)
	case ScreenAgentActions:
		return updateAgentActions(model, key)
	case ScreenLogs:
		return updateLogs(model, key)
	case ScreenServer:
		return updateServerScreen(model, key)
	case ScreenTests:
		return updateTestsScreen(model, key)
	case ScreenTestOutput:
		return updateTestOutput(model, key)
	}
	return model, nil
}

func updateMainScreen(model Model, key KeyMsg) (Model, []Intent) {
	opts := MainMenuOptions()
	switch key.Str {
	case "up", "k":
		model.Cursor = clamp(model.Cursor-1, 0, len(opts)-1)
	case "down", "j":
		model.Cursor = clamp(model.Cursor+1, 0, len(opts)-1)
	case "enter":
		switch opts[model.Cursor].Label {
		case "Agents":
			model.Screen = ScreenAgentList
			model.Cursor = 0
			return model, []Intent{{Kind: IntentLoadAgents}}
		case "Server":
			model.Screen = ScreenServer
			model.Cursor = 0
			model.StatusMsg = ""
			return model, []Intent{{Kind: IntentLoadAgents}}
		case "Tests":
			model.Screen = ScreenTests
			model.Cursor = 0
			model.StatusMsg = ""
			return model, nil
		case "Quit":
			return model, []Intent{{Kind: IntentQuit}}
		}
	}
	return model, nil
}

func updateAgentList(model Model, key KeyMsg) (Model, []Intent) {
	switch key.Str {
	case "0":
		model.Screen = ScreenMain
		model.Cursor = 0
		model.StatusMsg = ""
	case "up", "k":
		model.Cursor = clamp(model.Cursor-1, 0, max(0, len(model.Agents)-1))
	case "down", "j":
		model.Cursor = clamp(model.Cursor+1, 0, max(0, len(model.Agents)-1))
	case "enter":
		if model.Cursor < len(model.Agents) {
			sel := model.Agents[model.Cursor]
			model.Selected = &sel
			model.Screen = ScreenAgentActions
			model.Cursor = 0
			model.StatusMsg = ""
		}
	}
	return model, nil
}

func updateAgentActions(model Model, key KeyMsg) (Model, []Intent) {
	if model.Selected == nil {
		model.Screen = ScreenAgentList
		return model, nil
	}

	opts := AgentActionOptions(model.Selected.Enabled)

	switch key.Str {
	case "0":
		model.Screen = ScreenAgentList
		model.Cursor = 0
		model.Selected = nil
		model.StatusMsg = ""
		return model, []Intent{{Kind: IntentLoadAgents}}
	case "up", "k":
		model.Cursor = clamp(model.Cursor-1, 0, len(opts)-1)
	case "down", "j":
		model.Cursor = clamp(model.Cursor+1, 0, len(opts)-1)
	case "enter":
		if model.Cursor < len(opts) {
			return executeAction(model, opts[model.Cursor].Label)
		}
	}
	return model, nil
}

func executeAction(model Model, action string) (Model, []Intent) {
	id := model.Selected.ID
	switch action {
	case "Enable":
		model.StatusMsg = "Enabling " + id + "..."
		return model, []Intent{{Kind: IntentEnableAgent, AgentID: id}}
	case "Disable":
		model.StatusMsg = "Disabling " + id + "..."
		return model, []Intent{{Kind: IntentDisableAgent, AgentID: id}}
	case "Reload":
		model.StatusMsg = "Hot-reloading " + id + "..."
		return model, []Intent{{Kind: IntentReloadAgent, AgentID: id}}
	case "Restart":
		model.StatusMsg = "Restarting launcher (all agents)..."
		return model, []Intent{{Kind: IntentRestartAgent, AgentID: id}}
	case "Logs":
		model.Screen = ScreenLogs
		model.LogLines = nil
		model.LogScroll = 0
		model.Cursor = 0
		return model, []Intent{{Kind: IntentLoadLogs, AgentID: id}}
	}
	return model, nil
}

func updateLogs(model Model, key KeyMsg) (Model, []Intent) {
	switch key.Str {
	case "0":
		if model.Selected != nil {
			model.Screen = ScreenAgentActions
		} else {
			model.Screen = ScreenServer
		}
		model.Cursor = 0
		model.LogLines = nil
		model.LogScroll = 0
	case "up", "k":
		model.LogScroll = max(0, model.LogScroll-1)
	case "down", "j":
		maxScroll := max(0, len(model.LogLines)-visibleLogLines(model))
		model.LogScroll = min(model.LogScroll+1, maxScroll)
	case "r":
		return model, []Intent{{Kind: IntentLoadLogs}}
	}
	return model, nil
}

func updateServerScreen(model Model, key KeyMsg) (Model, []Intent) {
	opts := ServerMenuOptions(model.LauncherRunning)

	switch key.Str {
	case "0":
		model.Screen = ScreenMain
		model.Cursor = 0
		model.StatusMsg = ""
	case "up", "k":
		model.Cursor = clamp(model.Cursor-1, 0, len(opts)-1)
	case "down", "j":
		model.Cursor = clamp(model.Cursor+1, 0, len(opts)-1)
	case "enter":
		if model.Cursor < len(opts) {
			return executeServerAction(model, opts[model.Cursor].Label)
		}
	}
	return model, nil
}

func executeServerAction(model Model, action string) (Model, []Intent) {
	switch action {
	case "Reload All":
		model.StatusMsg = "Hot-reloading all agents..."
		return model, []Intent{{Kind: IntentReloadAll}}
	case "Start":
		model.StatusMsg = "Starting launcher..."
		return model, []Intent{{Kind: IntentStartLauncher}}
	case "Stop":
		model.StatusMsg = "Stopping launcher..."
		return model, []Intent{{Kind: IntentStopLauncher}}
	case "Restart":
		model.StatusMsg = "Restarting launcher..."
		return model, []Intent{{Kind: IntentRestartLauncher}}
	case "Kill":
		model.StatusMsg = "Killing launcher..."
		return model, []Intent{{Kind: IntentKillLauncher}}
	case "Rebuild & Restart":
		model.StatusMsg = "Building & restarting..."
		return model, []Intent{{Kind: IntentRebuildRestart}}
	case "Tests":
		model.Screen = ScreenTests
		model.Cursor = 0
		model.StatusMsg = ""
		return model, nil
	case "Logs":
		model.Screen = ScreenLogs
		model.LogLines = nil
		model.LogScroll = 0
		model.Selected = nil
		model.Cursor = 0
		return model, []Intent{{Kind: IntentLoadLogs}}
	}
	return model, nil
}

func updateTestOutput(model Model, key KeyMsg) (Model, []Intent) {
	switch key.Str {
	case "0":
		model.Screen = ScreenTests
		model.Cursor = 0
		model.LogLines = nil
		model.LogScroll = 0
		model.StatusMsg = ""
	case "up", "k":
		model.LogScroll = max(0, model.LogScroll-1)
	case "down", "j":
		maxScroll := max(0, len(model.LogLines)-visibleLogLines(model))
		model.LogScroll = min(model.LogScroll+1, maxScroll)
	case "r":
		intent := testKindIntent(model.LastTestKind)
		if intent == "" {
			intent = IntentRunGoTests
		}
		model.StatusMsg = "Running tests..."
		model.LogLines = nil
		model.LogScroll = 0
		return model, []Intent{{Kind: intent}}
	}
	return model, nil
}

func updateTestsScreen(model Model, key KeyMsg) (Model, []Intent) {
	opts := TestMenuOptions()

	switch key.Str {
	case "0":
		model.Screen = ScreenMain
		model.Cursor = 0
		model.StatusMsg = ""
	case "up", "k":
		model.Cursor = clamp(model.Cursor-1, 0, len(opts)-1)
	case "down", "j":
		model.Cursor = clamp(model.Cursor+1, 0, len(opts)-1)
	case "enter":
		if model.Cursor < len(opts) {
			return executeTestAction(model, opts[model.Cursor].Label)
		}
	}
	return model, nil
}

func executeTestAction(model Model, action string) (Model, []Intent) {
	model.StatusMsg = "Running tests..."
	model.LogLines = nil
	model.LogScroll = 0
	switch action {
	case "Go Tests":
		model.LastTestKind = TestKindGo
		return model, []Intent{{Kind: IntentRunGoTests}}
	case "E2E Tests":
		model.LastTestKind = TestKindE2E
		return model, []Intent{{Kind: IntentRunE2ETests}}
	case "E2E Tests (headed)":
		model.LastTestKind = TestKindE2EHead
		return model, []Intent{{Kind: IntentRunE2EHeadTests}}
	case "All Tests":
		model.LastTestKind = TestKindAll
		return model, []Intent{{Kind: IntentRunAllTests}}
	}
	return model, nil
}

// testKindIntent maps a TestKind to its corresponding IntentKind.
func testKindIntent(k TestKind) IntentKind {
	switch k {
	case TestKindGo:
		return IntentRunGoTests
	case TestKindE2E:
		return IntentRunE2ETests
	case TestKindE2EHead:
		return IntentRunE2EHeadTests
	case TestKindAll:
		return IntentRunAllTests
	default:
		return ""
	}
}

// testKindLabel returns a human-readable label for a TestKind.
func testKindLabel(k TestKind) string {
	switch k {
	case TestKindGo:
		return "Go Tests"
	case TestKindE2E:
		return "E2E Tests"
	case TestKindE2EHead:
		return "E2E Tests (headed)"
	case TestKindAll:
		return "All Tests"
	default:
		return "Tests"
	}
}

// ── pure helpers ─────────────────────────────────────────────────────────

func visibleLogLines(m Model) int {
	lines := m.WindowHeight - 6
	if lines < 5 {
		return 5
	}
	return lines
}

func clamp(v, lo, hi int) int {
	if v < lo {
		return lo
	}
	if v > hi {
		return hi
	}
	return v
}
