package tui

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"gitgud.io/mike/mpv-manager/pkg/config"
	"gitgud.io/mike/mpv-manager/pkg/constants"
	"gitgud.io/mike/mpv-manager/pkg/installer"
	"gitgud.io/mike/mpv-manager/pkg/log"
	"gitgud.io/mike/mpv-manager/pkg/version"
	"github.com/charmbracelet/bubbles/list"
	"github.com/charmbracelet/bubbles/textinput"
	tea "github.com/charmbracelet/bubbletea"
)

func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyCtrlC:
			m.installing = false
			m.selectedMethod = ""
			m.selectedMethodID = ""
			return m, tea.Quit

		case tea.KeyCtrlH:
			m.showHelp = !m.showHelp
			return m, nil

		case tea.KeyCtrlV:
			m.showVersion = !m.showVersion
			return m, nil

		case tea.KeyEnter:
			if m.state == StateMainMenu && !m.installing {
				selected := m.menuList.SelectedItem()
				if selected != nil {
					item := selected.(menuItem)
					m.selectedMethod = item.title
					m.selectedMethodID = item.id
					m.statusMessage = item.title

					switch m.selectedMethodID {
					case "updates":
						m.state = StateUpdates
						m.updateCheckResult = nil
						m.versionCheckResult = nil
						m.updateItems = []list.Item{}
						m.installerCheckType = "manager"
						return m, runUpdatesCheckCmd(m.installer)
					case "mpv-manager":
						m.setupInstallMenuList()
						m.state = StateInstallMenu
						m.statusMessage = "Select Installation Method"
						return m, nil
					case "unmpv-manager":
						m.state = StateUninstall
						return m, refreshUninstallList()
					case "install-to-path":
						m.state = StateInstallToPath
					case "set-language-preferences":
						m.loadLanguageData()
						m.setupLangPreferenceList()
						m.state = StateLanguagePreferences
						return m, nil
					case "config-options":
						m.setupConfigOptionsList()
						m.state = StateConfigOptions
						return m, nil
					case "hotkeys":
						m.setupHotkeysCategoryList()
						m.state = StateHotkeysCategory
						m.statusMessage = "Keyboard Shortcuts"
						return m, nil
					case "web-ui":
						m.setupWebUIList()
						m.state = StateWebUIMenu
						return m, nil
					}
				}
			} else if m.state == StateInstallMenu && !m.installing {
				selected := m.installMenuList.SelectedItem()
				if selected != nil {
					item := selected.(menuItem)
					m.selectedMethod = item.title
					m.selectedMethodID = item.id
					m.statusMessage = item.title

					// Check if this method needs UI selection
					if needsUISelection(m.selectedMethodID) {
						m.pendingMethodID = m.selectedMethodID
						m.pendingAppName = getAppNameForMethod(m.selectedMethodID)
						m.setupUISelectList()
						m.state = StateUISelect
						return m, nil
					}

					// Direct installation for non-MPV methods (IINA, MPC-QT, Celluloid)
					m.state = StateInstalling
					m.installProgress = 0
					m.installing = true
					m.installComplete = false
					m.installOperationType = OpTypeInstall

					installDir := m.getInstallPath()
					// Use default UI type for direct installations
					return m, executeInstallCmd(m.selectedMethodID, installDir, true, m.platformInfo, m.installer)

				}
			} else if m.state == StateUninstall && !m.installing {
				selected := m.uninstallList.SelectedItem()
				if selected != nil {
					appItem := selected.(uninstallAppItem)
					m.selectedUninstall = appItem.app
					m.statusMessage = appItem.app.AppName
					m.state = StateInstalling
					m.installProgress = 0
					m.installing = true
					m.installComplete = false

					return m, executeUninstallCmd(appItem.app, m.platformInfo, m.installer)
				}
			} else if m.state == StateUpdates && !m.installing {
				selected := m.updateList.SelectedItem()
				if selected != nil {
					switch item := selected.(type) {
					case menuItem:
						if item.id == "update-installer" {
							m.state = StateUpdateInstaller
							return m, checkForUpdateCmd()
						} else if item.id == "load-config" {
							m.state = StateInstalling
							m.loadingConfig = true
							m.installProgress = 0
							m.installing = true
							m.installComplete = false
							outputChan := make(chan string, 100)
							installDone := make(chan error, 1)

							go func() {
								cr := installer.NewCommandRunner(outputChan, nil)
								var err error
								err = m.installer.InstallMPVConfigWithOutput(cr)
								installDone <- err
								close(outputChan)
							}()

							return m, func() tea.Msg {
								return saveChannelsMsg{outputChan: outputChan, installDone: installDone}
							}
						}
					case updateItem:
						if item.UpdateType == "update" {
							m.state = StateInstalling
							m.installProgress = 0
							m.installing = true
							m.installComplete = false
							m.selectedUpdateItem = &item
							m.installOperationType = OpTypeUpdate
							return m, executeUpdateCmd(item.MethodID, item.AppName, m.platformInfo, m.installer)
						} else if item.UpdateType == "package-update" {
							m.state = StateInstalling
							m.installProgress = 0
							m.installing = true
							m.installComplete = false
							m.installOperationType = OpTypeUpdate
							return m, executeUpdateCmd(item.MethodID, item.AppName, m.platformInfo, m.installer)
						}
					case configBackupItem:
						m.selectedRestoreBackup = item.backup
						m.state = StateRestoreConfig

						items := make([]list.Item, len(m.updateCheckResult.ConfigBackupItems))
						for i, backup := range m.updateCheckResult.ConfigBackupItems {
							items[i] = configBackupItem{backup: &backup}
						}

						m.restoreConfigList = m.createStyledList("Select Backup to Restore", items)

						return m, nil
					}
				}
			} else if m.state == StateRestoreConfig && !m.installing {
				selected := m.restoreConfigList.SelectedItem()
				if selected != nil {
					backupItem := selected.(configBackupItem)
					m.selectedRestoreBackup = backupItem.backup
					m.state = StateInstalling
					m.installProgress = 0
					m.installing = true
					m.installComplete = false
					m.installOperationType = OpTypeConfig
					m.currentCommand = fmt.Sprintf("Restoring config from %s", m.selectedRestoreBackup.BackupName)
					return m, m.runRestoreConfigCmd()
				}
			} else if m.state == StateInstallToPath && !m.installing {
				m.state = StateInstalling
				m.installProgress = 0
				m.installing = true
				m.installComplete = false
				m.selectedMethodID = "install-to-path"

				isRemoving := isInstallMPVInPATH()
				return m, executePathCmd(isRemoving, m.installer)
			}

		case tea.KeyEsc:
			if m.state == StateError {
				m.state = StateMainMenu
				m.errorMessage = ""
				m.installOutput = []string{}
				m.currentCommand = ""
				return m, nil
			} else if m.state == StateInstallMenu {
				m.resetToMainMenu()
				return m, nil
			} else if m.state == StateInstalling {
				m.resetToMainMenu()
				return m, nil
			} else if m.state == StateSuccess {
				m.resetToMainMenu()
				return m, nil
			} else if m.state == StateUpdateInstaller || m.state == StateUninstall || m.state == StateInstallToPath {
				m.resetToMainMenu()
				return m, nil
			} else if m.state == StateUpdates {
				m.resetToMainMenu()
				return m, nil
			} else if m.state == StateConfigOptions {
				m.resetToMainMenu()
				return m, nil
			} else if m.state == StateHWAOptions {
				m.setupConfigOptionsList()
				m.state = StateConfigOptions
				return m, nil
			} else if m.state == StateScreenshotOptions {
				m.setupConfigOptionsList()
				m.state = StateConfigOptions
				return m, nil
			} else if m.state == StateRestoreConfig {
				m.setupConfigOptionsList()
				m.state = StateConfigOptions
				return m, nil
			} else if m.state == StateFileAssociations {
				m.setupConfigOptionsList()
				m.state = StateConfigOptions
				return m, nil
			} else if m.state == StateFileAssociationsMenu {
				m.setupConfigOptionsList()
				m.state = StateConfigOptions
				return m, nil
			} else if m.state == StateSuccess {
				m.resetToMainMenu()
				return m, nil
			} else if m.state == StateUpdateInstaller || m.state == StateUpdateList || m.state == StateUninstall || m.state == StateInstallToPath {
				m.resetToMainMenu()
				m.RefreshMainMenu()
				return m, nil
			}

		case tea.KeyRunes:
			if m.state == StateUpdateInstaller && len(msg.Runes) == 1 {
				switch string(msg.Runes[0]) {
				case "u", "U":
					if m.versionCheckResult != nil && m.versionCheckResult.UpdateAvailable {
						m.state = StateInstalling
						m.installProgress = 0
						m.installing = true
						m.installComplete = false
						m.downloadWritten = 0
						m.downloadTotal = 0
						m.currentCommand = "Updating MPV Manager"

						outputChan := make(chan string, 100)
						installDone := make(chan error, 1)
						progressChan := make(chan downloadProgressMsg, 10)

						go func() {
							execPath, err := os.Executable()
							if err != nil {
								outputChan <- fmt.Sprintf("Error: Failed to get executable path: %v", err)
								installDone <- err
								close(outputChan)
								close(progressChan)
								return
							}

							progressCallback := func(written, total int64) {
								progressChan <- downloadProgressMsg{written: written, total: total}
							}

							if err := version.UpdateSelfWithProgress(execPath, progressCallback); err != nil {
								outputChan <- fmt.Sprintf("Error: Failed to update manager: %v", err)
								installDone <- err
							} else {
								outputChan <- "✓ Manager updated successfully!"
								outputChan <- "✓ BLAKE3 hash verified."
								if config.GetManagerInPATH() {
									outputChan <- "✓ PATH installation also updated."
								}
								outputChan <- ""
								outputChan <- "Press ENTER to restart application..."
							}
							close(outputChan)
							close(progressChan)
						}()

						return m, func() tea.Msg {
							return saveChannelsMsg{
								outputChan:   outputChan,
								installDone:  installDone,
								progressChan: progressChan,
							}
						}
					}
				}
			}
		}

	case tea.WindowSizeMsg:
		m.termWidth = msg.Width
		m.termHeight = msg.Height
		m.viewport.Width = msg.Width
		m.viewport.Height = msg.Height
		m.outputViewport.Width = msg.Width - 10
		m.outputViewport.Height = msg.Height / 2
		m.menuList.SetWidth(msg.Width - 10)
		m.menuList.SetHeight(msg.Height - 15)
		m.installMenuList.SetWidth(msg.Width - 10)
		m.installMenuList.SetHeight(msg.Height - 15)
		m.uninstallList.SetWidth(msg.Width - 10)
		m.uninstallList.SetHeight(msg.Height - 15)
		m.updateList.SetWidth(msg.Width - 10)
		m.updateList.SetHeight(msg.Height - 15)
		m.restoreConfigList.SetWidth(msg.Width - 10)
		m.restoreConfigList.SetHeight(msg.Height - 15)
		m.screenshotOptionsList.SetWidth(msg.Width - 10)
		m.screenshotOptionsList.SetHeight(msg.Height - 15)
		m.uiSelectList.SetWidth(msg.Width - 10)
		m.uiSelectList.SetHeight(msg.Height - 15)

	case installErrorMsg:
		m.state = StateError
		m.errorMessage = msg.err.Error()
		m.installing = false
		return m, nil

	case versionCheckMsg:
		m.versionCheckResult = msg.result
		return m, nil

	case updateCheckResultMsg:
		m.updateCheckResult = msg.result

		items := make([]list.Item, 0, 2+len(msg.result.AppsToUpdate))

		installerItem := menuItem{
			id:    "update-installer",
			title: "🔄 Update MPV Manager",
		}
		if m.versionCheckResult != nil {
			if m.versionCheckResult.UpdateAvailable {
				installerItem.title = "🔄 Update MPV Manager " + successStyle.Render("[ Update Available ]")
				installerItem.description = fmt.Sprintf("Current: %s - Available: %s - Select to update", m.versionCheckResult.CurrentVersion, m.versionCheckResult.LatestVersion)
			} else if m.versionCheckResult.Error == nil {
				installerItem.description = fmt.Sprintf("Current: %s - Latest", m.versionCheckResult.CurrentVersion)
			} else {
				installerItem.description = "Error checking for updates"
			}
		} else {
			installerItem.description = "Checking for manager updates..."
		}
		items = append(items, installerItem)

		items = append(items, menuItem{
			id:          "load-config",
			title:       "📥 Load Latest Recommended MPV Config",
			description: "Install the latest recommended mpv.conf configuration",
		})

		for _, app := range msg.result.AppsToUpdate {
			items = append(items, updateItem{
				AppName:          app.AppName,
				CurrentVersion:   app.CurrentVersion,
				AvailableVersion: app.AvailableVersion,
				UpdateType:       app.UpdateType,
				PackageManager:   app.PackageManager,
				MethodID:         app.MethodID,
			})
		}

		m.updateList = m.createStyledList("Available Updates", items)

		return m, nil

	case uninstallAppsLoadedMsg:
		items := make([]list.Item, len(msg.apps))
		for i, app := range msg.apps {
			items[i] = uninstallAppItem{app: &app}
		}

		m.uninstallList.SetItems(items)
		m.uninstallList.Title = "Installed Applications"
		m.uninstallList.Styles.Title = titleStyle

		return m, nil

	case refreshMenuMsg:
		m.RefreshMainMenu()
		return m, tea.Tick(10*time.Millisecond, func(t time.Time) tea.Msg {
			return tickMsg{}
		})

	case cmdStartMsg:
		m.currentCommand = msg.command
		m.installOutput = append(m.installOutput, "")
		m.installOutput = append(m.installOutput, fmt.Sprintf("Running: %s", msg.command))
		m.installOutput = append(m.installOutput, strings.Repeat("─", 50))
		m.updateOutputViewport()
		return m, nil

	case saveChannelsMsg:
		m.outputChan = msg.outputChan
		m.installDone = msg.installDone
		if msg.progressChan != nil {
			m.progressChan = msg.progressChan
		}
		return m, startStreaming(m.outputChan, m.installDone, m.progressChan)

	case downloadProgressMsg:
		m.downloadWritten = msg.written
		m.downloadTotal = msg.total
		if m.downloadTotal > 0 {
			percent := float64(m.downloadWritten) / float64(m.downloadTotal) * 100
			m.installProgress = int(percent)
		}
		return m, startStreaming(m.outputChan, m.installDone, m.progressChan)

	case cmdOutputMsg:
		m.installOutput = append(m.installOutput, msg.line)
		m.updateOutputViewport()
		if m.autoScroll {
			m.outputViewport.GotoBottom()
		}
		return m, startStreaming(m.outputChan, m.installDone, nil)

	case cmdDoneMsg:
		m.installComplete = true
		m.installing = false
		m.loadingConfig = false
		m.installError = msg.err

		if postInstallMsg, ok := msg.err.(installer.PostInstallMessage); ok {
			m.postInstallMessage = postInstallMsg.Message
			m.installError = nil
		}

		if msg.err == nil {
			m.installSuccess = true

			// Handle successful install/update
			if m.selectedUninstall != nil {
				// ✅ SUCCESSFUL UNINSTALL
				log.Separator("Uninstallation Summary")
				log.Info(fmt.Sprintf("Uninstallation completed successfully: %s", m.selectedUninstall.AppName))
				log.Info("Removed from installed_apps config")
				config.RemoveInstalledApp(m.selectedUninstall.AppName)
				m.RefreshMainMenu()

				// Show success message to user
				m.installOutput = append(m.installOutput, "")
				m.installOutput = append(m.installOutput, "✓ Uninstallation completed successfully!")
				m.installOutput = append(m.installOutput, fmt.Sprintf("Removed: %s", m.selectedUninstall.AppName))
				m.installOutput = append(m.installOutput, "")
				m.installOutput = append(m.installOutput, "Press Enter to return to main menu or Esc to quit")
				m.updateOutputViewport()
			} else if m.selectedMethodID == "install-to-path" {
				log.Separator("PATH Operation Summary")
				binDir := ""
				if runtime.GOOS == "windows" {
					homeDir, _ := os.UserHomeDir()
					binDir = filepath.Join(homeDir, "bin")
				} else {
					homeDir, _ := os.UserHomeDir()
					binDir = filepath.Join(homeDir, ".local", "bin")
				}

				inPath := isInstallMPVInPATH()
				if inPath {
					log.Info("mpv-manager added to system PATH successfully")
					log.Info(fmt.Sprintf("Binary location: %s", filepath.Join(binDir, constants.AppName)))
					log.Info(fmt.Sprintf("Symlink location: %s", filepath.Join(binDir, "mpv-install")))
					config.SetManagerInPATH(true)
					config.SetManagerBinPath(filepath.Join(binDir, constants.AppName))
					m.installOutput = append(m.installOutput, "")
					m.installOutput = append(m.installOutput, "✓ mpv-manager added to system PATH successfully!")
					m.installOutput = append(m.installOutput, fmt.Sprintf("  Binary: %s", filepath.Join(binDir, constants.AppName)))
					m.installOutput = append(m.installOutput, fmt.Sprintf("  Symlink: %s", filepath.Join(binDir, "mpv-install")))
					m.installOutput = append(m.installOutput, "")
					m.installOutput = append(m.installOutput, "You can now run 'mpv-manager' or 'mpv-install' from anywhere!")
					m.installOutput = append(m.installOutput, "")
					m.installOutput = append(m.installOutput, "Press Enter to return to main menu or Esc to quit")
					m.updateOutputViewport()
				} else {
					log.Info("mpv-manager removed from system PATH successfully")
					config.SetManagerInPATH(false)
					config.SetManagerBinPath("")
					m.installOutput = append(m.installOutput, "")
					m.installOutput = append(m.installOutput, "✓ mpv-manager removed from system PATH successfully!")
					m.installOutput = append(m.installOutput, "")
					m.installOutput = append(m.installOutput, "Press Enter to return to main menu or Esc to quit")
					m.updateOutputViewport()
				}
				m.RefreshMainMenu()
			} else {
				// Handle successful install
				switch m.installOperationType {
				case OpTypeInstall:
					log.Separator("Installation Summary")
					log.Info(fmt.Sprintf("Installation completed successfully: %s", getClientName(m.selectedMethodID, m.selectedMethod)))
					log.Info(fmt.Sprintf("App saved to config: %s", m.selectedMethodID))
					m.saveInstalledApp()
					m.RefreshMainMenu()

				case OpTypeUpdate:
					log.Separator("Update Summary")
					log.Info(fmt.Sprintf("Update completed successfully: %s", getClientName(m.selectedMethodID, m.selectedMethod)))
					m.updateInstalledAppVersion()
					m.RefreshMainMenu()

				case OpTypeConfig:
					log.Separator("Config Operation Summary")
					log.Info("Configuration operation completed successfully")
					m.RefreshMainMenu()

				default:
					log.Warn("Unknown operation type completed")
					m.RefreshMainMenu()
				}
			}
		} else {
			// ✅ HANDLE ERRORS PROPERLY
			m.installSuccess = false

			if m.selectedUninstall != nil {
				// ❌ FAILED UNINSTALL
				log.Separator("Uninstallation Error")
				log.Error(fmt.Sprintf("Uninstallation failed: %v", msg.err))
				log.Info("App NOT removed from installed_apps config (uninstall did not complete)")

				// Show error message to user
				m.installOutput = append(m.installOutput, "")
				m.installOutput = append(m.installOutput, "✗ Uninstallation failed!")
				m.installOutput = append(m.installOutput, fmt.Sprintf("Error: %v", msg.err))
				m.installOutput = append(m.installOutput, "")
				m.installOutput = append(m.installOutput, "Press Enter to return to main menu or Esc to quit")
				m.updateOutputViewport()

				// Don't remove from config - app wasn't actually uninstalled
				// App will stay in menu for user to try again
				m.RefreshMainMenu()
			} else {
				// Handle failed install/update
				log.Error(fmt.Sprintf("Installation failed: %s", msg.err.Error()))

				// Show error message to user
				m.installOutput = append(m.installOutput, "")
				m.installOutput = append(m.installOutput, "✗ Installation failed!")
				m.installOutput = append(m.installOutput, fmt.Sprintf("Error: %v", msg.err))
				m.installOutput = append(m.installOutput, "")
				m.installOutput = append(m.installOutput, "Press Enter to return to main menu or Esc to quit")
				m.updateOutputViewport()
			}
		}

		// Reset operation type
		m.installOperationType = OpTypeNone

		return m, nil

	case tickMsg:

	case tea.MouseMsg:
		if m.state == StateInstalling {
			switch msg.Type {
			case tea.MouseWheelDown:
				m.outputViewport.LineDown(1)
				m.autoScroll = false
			case tea.MouseWheelUp:
				m.outputViewport.LineUp(1)
				m.autoScroll = false
			}
		}
	}

	if (m.state == StateMainMenu || m.state == StateInstallMenu || m.state == StateUninstall) && !m.installing {
		if m.state == StateMainMenu {
			m.menuList, cmd = m.menuList.Update(msg)
		} else if m.state == StateInstallMenu {
			m.installMenuList, cmd = m.installMenuList.Update(msg)
		} else if m.state == StateUninstall {
			m.uninstallList, cmd = m.uninstallList.Update(msg)
		}
	}

	// Delegate language state handling
	switch m.state {
	case StateLanguagePreferences:
		m, cmd = m.handleLangPreferenceUpdate(msg)
		return m, cmd
	case StateLanguageMajorSelect:
		m, cmd = m.handleMajorLangUpdate(msg)
		return m, cmd
	case StateLanguageRegionSelect:
		m, cmd = m.handleRegionLangUpdate(msg)
		return m, cmd
	case StateLanguageSelectedList:
		m, cmd = m.handleSelectedLangUpdate(msg)
		return m, cmd
	case StateLanguageConfirm:
		switch msg := msg.(type) {
		case tea.KeyMsg:
			switch msg.Type {
			case tea.KeyEnter:
				m.state = StateMainMenu
				return m, nil
			}
		}
		return m, nil
	case StateConfigOptions:
		m, cmd = m.handleConfigOptionsUpdate(msg)
		return m, cmd
	case StateHWAOptions:
		m, cmd = m.handleHWAOptionsUpdate(msg)
		return m, cmd
	case StateScreenshotOptions:
		m, cmd = m.handleScreenshotSettingsUpdate(msg)
		return m, cmd
	// Screenshot value selection states (dropdown lists)
	case StateScreenshotFormatEdit:
		fallthrough
	case StateScreenshotTagColorspaceEdit:
		fallthrough
	case StateScreenshotHighBitDepthEdit:
		fallthrough
	case StateScreenshotJpegQualityEdit:
		fallthrough
	case StateScreenshotPngCompressionEdit:
		fallthrough
	case StateScreenshotWebpLosslessEdit:
		fallthrough
	case StateScreenshotWebpQualityEdit:
		fallthrough
	case StateScreenshotJxlDistanceEdit:
		fallthrough
	case StateScreenshotAvifEncoderEdit:
		fallthrough
	case StateScreenshotAvifPixfmtEdit:
		m, cmd = m.handleScreenshotValueSelection(msg)
		return m, cmd
	// Screenshot text input states
	case StateScreenshotTemplateEdit:
		fallthrough
	case StateScreenshotDirectoryEdit:
		fallthrough
	case StateScreenshotAvifOptsEdit:
		m, cmd = m.handleScreenshotTextInput(msg)
		return m, cmd
	case StateSavePositionOnQuitEdit:
		m, cmd = m.handleSavePositionOnQuitUpdate(msg)
		return m, cmd
	case StateFileAssociationsMenu:
		m, cmd = m.handleFileAssociationsMenuUpdate(msg)
		return m, cmd
	case StateWebUIMenu:
		switch msg := msg.(type) {
		case tea.KeyMsg:
			switch msg.Type {
			case tea.KeyEnter:
				if !m.installing {
					selected := m.webUIList.SelectedItem()
					if selected != nil {
						item := selected.(menuItem)
						m.selectedMethodID = item.id
						m.statusMessage = item.title

						switch m.selectedMethodID {
						case "create-webui-shortcut":
							return m, m.runCreateWebUIShortcutCmd()
						}
					}
				}
			case tea.KeyEsc:
				m.state = StateMainMenu
				m.statusMessage = "Main Menu"
			}
		}
		m.webUIList, cmd = m.webUIList.Update(msg)
		return m, cmd

	case StateHotkeysCategory:
		m, cmd = m.handleHotkeysCategoryUpdate(msg)
		return m, cmd

	case StateHotkeys:
		m, cmd = m.handleHotkeysUpdate(msg)
		return m, cmd

	case StateUISelect:
		m, cmd = m.handleUISelectUpdate(msg)
		return m, cmd

	case StateInstalling:
		if m.installComplete {
			isUpdate := strings.HasPrefix(m.currentCommand, "Updating")
			switch msg := msg.(type) {
			case tea.KeyMsg:
				switch msg.Type {
				case tea.KeyEnter:
					if isUpdate && m.installError == nil {
						return m, tea.Quit
					} else {
						m.resetToMainMenu()
						return m, nil
					}
				case tea.KeyEsc:
					m.resetToMainMenu()
					return m, nil
				}
			}
		}
		return m, nil

	case StateUpdates:
		if m.updateList.SettingFilter() || m.updateList.IsFiltered() {
			switch msg := msg.(type) {
			case tea.KeyMsg:
				if msg.Type == tea.KeyEsc {
					m.updateList.ResetFilter()
					return m, nil
				}
			}
		}

		var cmd tea.Cmd
		m.updateList, cmd = m.updateList.Update(msg)
		return m, cmd

	case StateUpdateList:
		if m.updateList.SettingFilter() || m.updateList.IsFiltered() {
			switch msg := msg.(type) {
			case tea.KeyMsg:
				if msg.Type == tea.KeyEsc {
					m.updateList.ResetFilter()
					return m, nil
				}
			}
		}

		var cmd tea.Cmd
		m.updateList, cmd = m.updateList.Update(msg)
		return m, cmd

	case StateRestoreConfig:
		if m.restoreConfigList.SettingFilter() || m.restoreConfigList.IsFiltered() {
			switch msg := msg.(type) {
			case tea.KeyMsg:
				if msg.Type == tea.KeyEsc {
					m.restoreConfigList.ResetFilter()
					return m, nil
				}
			}
		}

		var cmd tea.Cmd
		m.restoreConfigList, cmd = m.restoreConfigList.Update(msg)
		return m, cmd
	}

	return m, cmd
}

func (m *Model) saveInstalledApp() {
	if m.selectedMethodID == "install-to-path" {
		return
	}

	appName := getClientName(m.selectedMethodID, m.selectedMethod)
	installType := m.getInstallType()
	installPath := m.getInstallPath()

	// Get version from appropriate source
	// Note: Don't save version for package-manager, flatpak, and snap
	// since these can be updated outside of our app
	appVersion := ""
	if installType != "package-manager" && installType != "flatpak" && installType != "snap" {
		appVersion = m.getInstalledAppVersion()
	}

	app := config.InstalledApp{
		AppName:       appName,
		AppType:       installType,
		InstallMethod: m.selectedMethodID,
		InstallPath:   installPath,
		InstalledDate: time.Now().Format("2006-01-02 15:04:05"),
		AppVersion:    appVersion,
	}

	if err := config.AddInstalledApp(app); err != nil {
		fmt.Printf("Error saving installed app: %v\n", err)
	}
}

func (m *Model) getInstallType() string {
	if meta, ok := MethodIDMetadata[m.selectedMethodID]; ok {
		return meta.Type
	}
	return ""
}

func (m *Model) getInstalledAppVersion() string {
	if m.installer != nil {
		if meta, ok := MethodIDMetadata[m.selectedMethodID]; ok && meta.Version != "" {
			switch meta.Version {
			case "mpv":
				return m.installer.ReleaseInfo.MpvVersion
			case "mpc-qt":
				return m.installer.ReleaseInfo.MPCQT.AppVersion
			case "iina":
				return m.installer.ReleaseInfo.IINA.AppVersion
			}
		}
	}
	return ""
}

func (m *Model) getInstallPath() string {
	if meta, ok := MethodIDMetadata[m.selectedMethodID]; ok {
		if meta.Type == "exe-binary" || meta.Type == "exe-installer" {
			return config.GetInstallPath()
		}
	}
	return ""
}

func (m *Model) updateInstalledAppVersion() {
	appVersion := m.getInstalledAppVersion()
	if appVersion == "" {
		return
	}

	cfg, err := config.Load()
	if err != nil {
		log.Warn(fmt.Sprintf("Failed to load config: %v", err))
		return
	}

	found := false
	for i, app := range cfg.InstalledApps {
		if app.InstallMethod == m.selectedMethodID {
			cfg.InstalledApps[i].AppVersion = appVersion
			found = true
			log.Separator("Update Summary")
			log.Info(fmt.Sprintf("Version updated successfully: %s", app.AppName))
			log.Info(fmt.Sprintf("New version: %s", appVersion))
			break
		}
	}

	if found {
		if err := config.Save(); err != nil {
			log.Warn(fmt.Sprintf("Failed to save config: %v", err))
		}
	} else {
		log.Warn(fmt.Sprintf("App not found for update: %s", m.selectedMethodID))
	}
}

func (m *Model) resetToMainMenu() {
	m.state = StateMainMenu
	m.selectedMethod = ""
	m.selectedMethodID = ""
	m.installing = false
	m.installComplete = false
	m.installOutput = []string{}
	m.currentCommand = ""
	m.selectedUninstall = nil
	m.selectedRestoreBackup = nil
	m.postInstallMessage = ""
	m.pendingMethodID = ""
	m.pendingAppName = ""
	m.selectedUIType = ""
}

func (m *Model) handleConfigOptionsUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	m.configOptionsList, cmd = m.configOptionsList.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEsc:
			m.state = StateMainMenu
			return m, nil
		case tea.KeyEnter:
			selected := m.configOptionsList.SelectedItem()
			if selected != nil {
				item := selected.(menuItem)
				switch item.id {
				case "reset-config":
					return m, m.runResetConfigCmd()
				case "restore-config":
					if err := m.loadAndSetupBackupList(); err != nil {
						m.errorMessage = "Failed to load config backups: " + err.Error()
						m.state = StateError
						return m, nil
					}
					m.state = StateRestoreConfig
					return m, nil
				case "change-hwdec":
					m.setupHWAOptionsList()
					m.state = StateHWAOptions
					return m, nil
				case "save-position-on-quit":
					m.setupSavePositionOnQuitList()
					m.state = StateSavePositionOnQuitEdit
					return m, nil
				case "screenshot-settings":
					m.setupScreenshotOptionsList()
					m.state = StateScreenshotOptions
					return m, nil
				case "file-associations":
					m.setupFileAssociationsList()
					m.state = StateFileAssociationsMenu
					return m, nil
				case "change-mpv-ui":
					// Find the first MPV app to change UI for
					apps := config.GetInstalledApps()
					mpvMethods := []string{
						"mpv-flatpak",
						"mpv-snap",
						"mpv-package",
						"mpv-binary",
						"mpv-binary-v3",
						"mpv-app",
						"mpv-brew",
					}
					for _, app := range apps {
						for _, method := range mpvMethods {
							if app.InstallMethod == method {
								m.pendingMethodID = method
								m.pendingAppName = app.AppName
								m.setupUISelectList()
								m.state = StateUISelect
								return m, nil
							}
						}
					}
					return m, nil
				}
			}
		}
	}

	return m, cmd
}

func (m *Model) handleHWAOptionsUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	m.hwaOptionsList, cmd = m.hwaOptionsList.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEsc:
			m.setupConfigOptionsList()
			m.state = StateConfigOptions
			return m, nil
		case tea.KeyEnter:
			selected := m.hwaOptionsList.SelectedItem()
			if selected != nil {
				item := selected.(hwaOptionItem)
				m.selectedHWAOption = item.id
				return m, m.runApplyHWACmd()
			}
		}
	}

	return m, cmd
}

func (m *Model) handleSavePositionOnQuitUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	m.screenshotValueList, cmd = m.screenshotValueList.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEsc:
			m.setupConfigOptionsList()
			m.state = StateConfigOptions
			return m, nil
		case tea.KeyEnter:
			selected := m.screenshotValueList.SelectedItem()
			if selected != nil {
				item := selected.(screenshotValueItem)
				// Apply the setting
				if err := config.SetConfigValue(constants.ConfigKeySavePositionOnQuit, []string{item.id}, false); err != nil {
					m.errorMessage = "Failed to apply save-position-on-quit setting: " + err.Error()
					m.state = StateError
					return m, nil
				}
				m.statusMessage = "Save Position on Quit set to: " + item.id
				m.setupConfigOptionsList()
				m.state = StateConfigOptions
				return m, nil
			}
		}
	}

	return m, cmd
}

func (m *Model) handleHotkeysCategoryUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	m.hotkeysCategoryList, cmd = m.hotkeysCategoryList.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEsc:
			m.state = StateMainMenu
			m.statusMessage = "Main Menu"
			return m, nil
		case tea.KeyEnter:
			selected := m.hotkeysCategoryList.SelectedItem()
			if selected != nil {
				item := selected.(hotkeyCategoryItem)
				m.setupHotkeysList(item.id)
				m.statusMessage = item.title
				m.state = StateHotkeys
				return m, nil
			}
		}
	}

	return m, cmd
}

func (m *Model) handleHotkeysUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	m.hotkeysList, cmd = m.hotkeysList.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEsc:
			m.setupHotkeysCategoryList()
			m.state = StateHotkeysCategory
			m.statusMessage = "Keyboard Shortcuts"
			return m, nil
		}
	}

	return m, cmd
}

func (m *Model) handleFileAssociationsMenuUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	m.fileAssociationsList, cmd = m.fileAssociationsList.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEsc:
			m.setupConfigOptionsList()
			m.state = StateConfigOptions
			return m, nil
		case tea.KeyEnter:
			selected := m.fileAssociationsList.SelectedItem()
			if selected != nil {
				item := selected.(menuItem)
				switch item.id {
				case "install-associations":
					return m, m.runInstallAssociationsCmd()
				case "remove-associations":
					return m, m.runRemoveAssociationsCmd()
				}
			}
		}
	}

	return m, cmd
}

func (m *Model) handleScreenshotSettingsUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEnter:
			selected := m.screenshotOptionsList.SelectedItem()
			if selected != nil {
				if option, ok := selected.(screenshotOptionItem); ok {
					m.selectedScreenshotOption = option.id

					// Set up editing interface based on option type
					switch option.id {
					case "screenshot-format":
						m.setupScreenshotFormatList()
						m.state = StateScreenshotFormatEdit
					case "screenshot-tag-colorspace":
						m.setupScreenshotYesNoList("tag-colorspace", "Tag Colorspace in Filename")
						m.state = StateScreenshotTagColorspaceEdit
					case "screenshot-high-bit-depth":
						m.setupScreenshotYesNoList("high-bit-depth", "High Bit Depth")
						m.state = StateScreenshotHighBitDepthEdit
					case "screenshot-template":
						// Initialize textinput with current value
						currentVal := getCurrentScreenshotValue("template")
						if currentVal == "Not set" {
							currentVal = "mpv-shot%n"
						}
						ti := textinput.New()
						ti.Placeholder = "Enter filename template..."
						ti.SetValue(currentVal)
						ti.CharLimit = 156
						ti.Width = 50
						ti.Focus()
						m.screenshotTextInput = ti
						m.state = StateScreenshotTemplateEdit
					case "screenshot-directory":
						// Initialize textinput with current value
						currentVal := getCurrentScreenshotValue("directory")
						if currentVal == "Not set" {
							currentVal = ""
						}
						ti := textinput.New()
						ti.Placeholder = "Enter directory path..."
						ti.SetValue(currentVal)
						ti.CharLimit = 256
						ti.Width = 50
						ti.Focus()
						m.screenshotTextInput = ti
						m.state = StateScreenshotDirectoryEdit
					case "screenshot-jpeg-quality":
						values := make([]string, 101)
						for i := 0; i <= 100; i++ {
							values[i] = fmt.Sprintf("%d", i)
						}
						m.setupScreenshotNumericList("jpeg-quality", "JPEG Quality (0-100)", values)
						m.state = StateScreenshotJpegQualityEdit
					case "screenshot-png-compression":
						values := make([]string, 10)
						for i := 0; i <= 9; i++ {
							values[i] = fmt.Sprintf("%d", i)
						}
						m.setupScreenshotNumericList("png-compression", "PNG Compression (0-9)", values)
						m.state = StateScreenshotPngCompressionEdit
					case "screenshot-webp-lossless":
						m.setupScreenshotYesNoList("webp-lossless", "WebP Lossless")
						m.state = StateScreenshotWebpLosslessEdit
					case "screenshot-webp-quality":
						values := make([]string, 101)
						for i := 0; i <= 100; i++ {
							values[i] = fmt.Sprintf("%d", i)
						}
						m.setupScreenshotNumericList("webp-quality", "WebP Quality (0-100)", values)
						m.state = StateScreenshotWebpQualityEdit
					case "screenshot-jxl-distance":
						values := []string{"0.0", "0.1", "0.2", "0.3", "0.4", "0.5", "1.0", "1.5", "2.0", "2.5", "3.0", "4.0", "5.0", "10.0", "15.0", "20.0", "25.0"}
						m.setupScreenshotNumericList("jxl-distance", "JPEG XL Distance", values)
						m.state = StateScreenshotJxlDistanceEdit
					case "screenshot-avif-encoder":
						m.setupScreenshotEncoderList()
						m.state = StateScreenshotAvifEncoderEdit
					case "screenshot-avif-pixfmt":
						m.setupScreenshotPixelFormatList()
						m.state = StateScreenshotAvifPixfmtEdit
					case "screenshot-avif-opts":
						// Initialize textinput with current value
						currentVal := getCurrentScreenshotValue("avif-opts")
						if currentVal == "Not set" {
							currentVal = "usage=allintra,crf=0,cpu-used=8"
						}
						ti := textinput.New()
						ti.Placeholder = "Enter encoder options..."
						ti.SetValue(currentVal)
						ti.CharLimit = 256
						ti.Width = 50
						ti.Focus()
						m.screenshotTextInput = ti
						m.state = StateScreenshotAvifOptsEdit
					}
					return m, nil
				}
			}
		case tea.KeyEsc:
			m.setupConfigOptionsList()
			m.state = StateConfigOptions
			return m, nil
		}
	}

	m.screenshotOptionsList, cmd = m.screenshotOptionsList.Update(msg)
	return m, cmd
}

// Generic handler for screenshot value selection (dropdown lists)
func (m *Model) handleScreenshotValueSelection(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	m.screenshotValueList, cmd = m.screenshotValueList.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEnter:
			selected := m.screenshotValueList.SelectedItem()
			if selected != nil {
				if val, ok := selected.(screenshotValueItem); ok {
					// Apply selected value
					key := getScreenshotConfigKey(m.selectedScreenshotOption)
					if err := config.SetConfigValue(key, []string{val.id}, false); err != nil {
						m.errorMessage = fmt.Sprintf("Failed to set %s: %v", key, err)
						m.state = StateError
						return m, nil
					}
					// Return to screenshot options list and refresh
					m.setupScreenshotOptionsList()
					m.state = StateScreenshotOptions
				}
			}
		case tea.KeyEsc:
			m.setupScreenshotOptionsList()
			m.state = StateScreenshotOptions
			return m, nil
		}
	}

	return m, cmd
}

// Handler for screenshot text input fields
func (m *Model) handleScreenshotTextInput(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd
	var tiCmd tea.Cmd
	m.screenshotTextInput, tiCmd = m.screenshotTextInput.Update(msg)

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEnter:
			// Apply the entered value
			value := m.screenshotTextInput.Value()
			key := getScreenshotConfigKey(m.selectedScreenshotOption)
			if err := config.SetConfigValue(key, []string{value}, false); err != nil {
				m.errorMessage = fmt.Sprintf("Failed to set %s: %v", key, err)
				m.state = StateError
				return m, nil
			}
			// Return to screenshot options list and refresh
			m.setupScreenshotOptionsList()
			m.state = StateScreenshotOptions
		case tea.KeyEsc:
			m.setupScreenshotOptionsList()
			m.state = StateScreenshotOptions
			return m, nil
		}
	}

	return m, tea.Batch(cmd, tiCmd)
}

func (m *Model) runResetConfigCmd() tea.Cmd {
	m.statusMessage = "Resetting MPV config to recommended..."
	m.installing = true
	m.state = StateInstalling
	m.installOperationType = OpTypeConfig

	outputChan := make(chan string)
	installDone := make(chan error)

	return func() tea.Msg {
		go func() {
			cr := installer.NewCommandRunner(outputChan, nil)
			err := m.installer.InstallMPVConfigWithOutput(cr)
			installDone <- err
			close(outputChan)
		}()
		return saveChannelsMsg{
			outputChan:  outputChan,
			installDone: installDone,
		}
	}

}

func (m *Model) runApplyHWACmd() tea.Cmd {
	m.statusMessage = "Applying hardware acceleration setting..."
	m.installing = true
	m.state = StateInstalling
	m.installOperationType = OpTypeConfig

	outputChan := make(chan string)
	installDone := make(chan error)

	return func() tea.Msg {
		go func() {
			outputChan <- fmt.Sprintf("Setting hwdec=%s", m.selectedHWAOption)
			if err := m.applyHWAOption(m.selectedHWAOption); err != nil {
				outputChan <- "Failed to apply hardware acceleration: " + err.Error()
				installDone <- err
				close(outputChan)
				return
			}

			outputChan <- fmt.Sprintf("Hardware acceleration set to: %s", m.selectedHWAOption)
			outputChan <- "✓ Configuration updated successfully"
			installDone <- nil
			close(outputChan)
		}()

		return saveChannelsMsg{
			outputChan:  outputChan,
			installDone: installDone,
		}
	}
}

func (m *Model) runRestoreConfigCmd() tea.Cmd {
	outputChan := make(chan string)
	installDone := make(chan error)

	return func() tea.Msg {
		go func() {
			cr := installer.NewCommandRunner(outputChan, nil)
			var err error
			if m.selectedRestoreBackup != nil {
				err = m.installer.RestoreConfigWithOutput(cr, m.selectedRestoreBackup.BackupPath)
			}
			installDone <- err
			close(outputChan)
		}()
		return saveChannelsMsg{
			outputChan:  outputChan,
			installDone: installDone,
		}
	}
}

func (m *Model) runInstallAssociationsCmd() tea.Cmd {
	m.statusMessage = "Installing File Associations"
	m.installing = true
	m.state = StateInstalling
	m.installOperationType = OpTypeConfig

	outputChan := make(chan string)
	installDone := make(chan error)

	return func() tea.Msg {
		go func() {
			handler := installer.NewInstallationHandler(m.platformInfo, m.installer)
			cr := installer.NewCommandRunner(outputChan, nil)

			err := handler.SetupFileAssociationsWithOutput(cr)
			installDone <- err
			close(outputChan)
		}()
		return saveChannelsMsg{
			outputChan:  outputChan,
			installDone: installDone,
		}
	}
}

func (m *Model) runRemoveAssociationsCmd() tea.Cmd {
	m.statusMessage = "Removing File Associations"
	m.installing = true
	m.state = StateInstalling
	m.installOperationType = OpTypeConfig

	outputChan := make(chan string)
	installDone := make(chan error)

	return func() tea.Msg {
		go func() {
			handler := installer.NewInstallationHandler(m.platformInfo, m.installer)
			cr := installer.NewCommandRunner(outputChan, nil)

			err := handler.RemoveFileAssociationsWithOutput(cr)
			installDone <- err
			close(outputChan)
		}()
		return saveChannelsMsg{
			outputChan:  outputChan,
			installDone: installDone,
		}
	}
}

func (m *Model) runCreateWebUIShortcutCmd() tea.Cmd {
	m.statusMessage = "Creating Web UI Shortcut"
	m.installing = true
	m.state = StateInstalling
	m.installOperationType = OpTypeConfig

	outputChan := make(chan string)
	installDone := make(chan error)

	return func() tea.Msg {
		go func() {
			handler := installer.NewInstallationHandler(m.platformInfo, m.installer)
			cr := installer.NewCommandRunner(outputChan, nil)

			err := handler.CreateWebUIShortcutWithOutput(cr)
			installDone <- err
			close(outputChan)
		}()
		return saveChannelsMsg{
			outputChan:  outputChan,
			installDone: installDone,
		}
	}
}

func (m *Model) loadAndSetupBackupList() error {
	backupDir := config.GetMPVConfigPath() + "/conf_backups"
	backupDir = strings.ReplaceAll(backupDir, "~", "")

	files, err := os.ReadDir(backupDir)
	if err != nil {
		return err
	}

	var items []list.Item
	for _, file := range files {
		fileName := file.Name()
		if strings.HasSuffix(fileName, ".conf") {
			backupPath := filepath.Join(backupDir, fileName)
			info, err := os.Stat(backupPath)
			if err != nil {
				continue
			}

			backupName := strings.TrimSuffix(fileName, ".conf")
			backup := version.ConfigBackupItem{
				BackupName: backupName,
				BackupPath: backupPath,
				BackupDate: info.ModTime().Format("2006-01-02 15:04"),
			}
			items = append(items, configBackupItem{backup: &backup})
		}
	}

	m.restoreConfigList = m.createStyledList("Select Backup to Restore", items)
	return nil
}

// handleUISelectUpdate handles the UI selection state
func (m *Model) handleUISelectUpdate(msg tea.Msg) (*Model, tea.Cmd) {
	var cmd tea.Cmd

	switch msg := msg.(type) {
	case tea.KeyMsg:
		switch msg.Type {
		case tea.KeyEnter:
			selected := m.uiSelectList.SelectedItem()
			if selected != nil {
				uiItem := selected.(uiOptionItem)
				m.selectedUIType = uiItem.id
				m.state = StateInstalling
				m.installProgress = 0
				m.installing = true
				m.installComplete = false
				m.installOperationType = OpTypeInstall

				// Set the selected method for proper tracking
				m.selectedMethodID = m.pendingMethodID
				m.selectedMethod = getAppNameForMethod(m.pendingMethodID)

				installDir := m.getInstallPath()
				return m, executeInstallCmdWithUI(m.pendingMethodID, installDir, m.selectedUIType, m.platformInfo, m.installer)
			}
		case tea.KeyEsc:
			// Go back to install menu
			m.state = StateInstallMenu
			m.pendingMethodID = ""
			m.pendingAppName = ""
			return m, nil
		}
	}

	m.uiSelectList, cmd = m.uiSelectList.Update(msg)
	return m, cmd
}
