package tui

import (
	"context"
	"fmt"
	"io"
	"os"
	"strings"
	"sync"

	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/mattn/go-isatty"

	"dbbackup/internal/cleanup"
	"dbbackup/internal/config"
	"dbbackup/internal/logger"
)

// IsInteractiveTerminal checks if stdout is an interactive terminal
// Returns false for: pipes, screen sessions (backgrounded), tmux detached, etc.
func IsInteractiveTerminal() bool {
	return isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd())
}

// Style definitions
var (
	titleStyle = lipgloss.NewStyle().
			Bold(true).
			Foreground(lipgloss.Color("15")).
			Background(lipgloss.Color("240")).
			Padding(0, 1)

	menuStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("250"))

	menuSelectedStyle = lipgloss.NewStyle().
				Foreground(lipgloss.Color("15")).
				Bold(true)

	infoStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("244"))

	successStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("2")).
			Bold(true)

	errorStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("1")).
			Bold(true)

	dbSelectorLabelStyle = lipgloss.NewStyle().
				Foreground(lipgloss.Color("6")).
				Bold(true)
)

type dbTypeOption struct {
	label string
	value string
}

// MenuModel represents the simple menu state
type MenuModel struct {
	choices      []string
	cursor       int
	config       *config.Config
	logger       logger.Logger
	quitting     bool
	message      string
	dbTypes      []dbTypeOption
	dbTypeCursor int

	// Background operations
	ctx       context.Context
	cancel    context.CancelFunc
	closeOnce sync.Once
}

func NewMenuModel(cfg *config.Config, log logger.Logger) *MenuModel {
	ctx, cancel := context.WithCancel(context.Background())

	dbTypes := []dbTypeOption{
		{label: "PostgreSQL", value: "postgres"},
		{label: "MySQL", value: "mysql"},
		{label: "MariaDB", value: "mariadb"},
	}

	dbCursor := 0
	if cfg.DatabaseType == "mysql" {
		dbCursor = 1
	} else if cfg.DatabaseType == "mariadb" {
		dbCursor = 2
	}

	model := &MenuModel{
		choices: []string{
			"Single Database Backup",
			"Sample Database Backup (with ratio)",
			"Cluster Backup (all databases)",
			"--------------------------------",
			"Restore Single Database",
			"Restore Cluster Backup",
			"Diagnose Backup File",
			"List & Manage Backups",
			"View Backup Schedule",
			"View Backup Chain",
			"--------------------------------",
			"System Resource Profile",
			"Tools",
			"View Active Operations",
			"Show Operation History",
			"Database Status & Health Check",
			"Configuration Settings",
			"Clear Operation History",
			"Quit",
		},
		config:       cfg,
		logger:       log,
		ctx:          ctx,
		cancel:       cancel,
		dbTypes:      dbTypes,
		dbTypeCursor: dbCursor,
	}

	return model
}

// Close implements io.Closer for safe cleanup
func (m *MenuModel) Close() error {
	m.closeOnce.Do(func() {
		if m.cancel != nil {
			m.cancel()
		}
	})
	return nil
}

// Ensure MenuModel implements io.Closer
var _ io.Closer = (*MenuModel)(nil)

// autoSelectMsg is sent when auto-select should trigger
type autoSelectMsg struct{}

// Init initializes the model
func (m *MenuModel) Init() tea.Cmd {
	// Auto-select menu option if specified
	if m.config.TUIAutoSelect >= 0 && m.config.TUIAutoSelect < len(m.choices) {
		m.logger.Info("TUI Auto-select enabled", "option", m.config.TUIAutoSelect, "label", m.choices[m.config.TUIAutoSelect])

		// Return command to trigger auto-selection
		return func() tea.Msg {
			return autoSelectMsg{}
		}
	}
	return nil
}

// Update handles messages
func (m *MenuModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	switch msg := msg.(type) {
	case autoSelectMsg:
		// Handle auto-selection
		if m.config.TUIAutoSelect >= 0 && m.config.TUIAutoSelect < len(m.choices) {
			m.cursor = m.config.TUIAutoSelect
			m.logger.Info("Auto-selecting option", "cursor", m.cursor, "choice", m.choices[m.cursor])

			// Trigger the selection based on cursor position
			switch m.cursor {
			case 0: // Single Database Backup
				return m.handleSingleBackup()
			case 1: // Sample Database Backup
				return m.handleSampleBackup()
			case 2: // Cluster Backup
				return m.handleClusterBackup()
			case 4: // Restore Single Database
				return m.handleRestoreSingle()
			case 5: // Restore Cluster Backup
				return m.handleRestoreCluster()
			case 6: // Diagnose Backup File
				return m.handleDiagnoseBackup()
			case 7: // List & Manage Backups
				return m.handleBackupManager()
			case 9: // Tools
				return m.handleTools()
			case 10: // View Active Operations
				return m.handleViewOperations()
			case 11: // Show Operation History
				return m.handleOperationHistory()
			case 12: // Database Status
				return m.handleStatus()
			case 13: // Settings
				return m.handleSettings()
			case 14: // Clear History
				m.message = "[DEL] History cleared"
			case 15: // Quit
				if m.cancel != nil {
					m.cancel()
				}
				m.quitting = true
				return m, tea.Quit
			}
		}
		return m, nil

	case tea.InterruptMsg:
		// Handle Ctrl+C signal (SIGINT) - Bubbletea v1.3+ sends this
		if m.cancel != nil {
			m.cancel()
		}

		// Clean up any orphaned processes before exit
		m.logger.Info("Cleaning up processes before exit (SIGINT)")
		if err := cleanup.KillOrphanedProcesses(m.logger); err != nil {
			m.logger.Warn("Failed to clean up all processes", "error", err)
		}

		m.quitting = true
		return m, tea.Quit

	case tea.KeyMsg:
		switch msg.String() {
		case "ctrl+c", "q":
			// Cancel all running operations
			if m.cancel != nil {
				m.cancel()
			}

			// Clean up any orphaned processes before exit
			m.logger.Info("Cleaning up processes before exit")
			if err := cleanup.KillOrphanedProcesses(m.logger); err != nil {
				m.logger.Warn("Failed to clean up all processes", "error", err)
			}

			m.quitting = true
			return m, tea.Quit

		case "left", "h":
			if m.dbTypeCursor > 0 {
				m.dbTypeCursor--
				m.applyDatabaseSelection()
			}

		case "right", "l":
			if m.dbTypeCursor < len(m.dbTypes)-1 {
				m.dbTypeCursor++
				m.applyDatabaseSelection()
			}

		case "t":
			if len(m.dbTypes) > 0 {
				m.dbTypeCursor = (m.dbTypeCursor + 1) % len(m.dbTypes)
				m.applyDatabaseSelection()
			}

		case "up", "k":
			if m.cursor > 0 {
				m.cursor--
			}

		case "down", "j":
			if m.cursor < len(m.choices)-1 {
				m.cursor++
			}

		case "enter", " ":
			switch m.cursor {
			case 0: // Single Database Backup
				return m.handleSingleBackup()
			case 1: // Sample Database Backup
				return m.handleSampleBackup()
			case 2: // Cluster Backup
				return m.handleClusterBackup()
			case 3: // Separator
				// Do nothing
			case 4: // Restore Single Database
				return m.handleRestoreSingle()
			case 5: // Restore Cluster Backup
				return m.handleRestoreCluster()
			case 6: // Diagnose Backup File
				return m.handleDiagnoseBackup()
			case 7: // List & Manage Backups
				return m.handleBackupManager()
			case 8: // View Backup Schedule
				return m.handleSchedule()
			case 9: // View Backup Chain
				return m.handleChain()
			case 10: // System Resource Profile
				return m.handleProfile()
			case 11: // Separator
				// Do nothing
			case 12: // Tools
				return m.handleTools()
			case 13: // View Active Operations
				return m.handleViewOperations()
			case 14: // Show Operation History
				return m.handleOperationHistory()
			case 15: // Database Status
				return m.handleStatus()
			case 16: // Settings
				return m.handleSettings()
			case 17: // Clear History
				m.message = "[DEL] History cleared"
			case 18: // Quit
				if m.cancel != nil {
					m.cancel()
				}
				m.quitting = true
				return m, tea.Quit
			}
		}
	}

	return m, nil
}

// View renders the simple menu
func (m *MenuModel) View() string {
	if m.quitting {
		return "Thanks for using DB Backup Tool!\n"
	}

	var s string

	// Product branding header
	brandLine := fmt.Sprintf("dbbackup v%s • Enterprise Database Backup & Recovery", m.config.Version)
	s += "\n" + infoStyle.Render(brandLine) + "\n"

	// Header
	header := titleStyle.Render("Interactive Menu")
	s += fmt.Sprintf("%s\n\n", header)

	if len(m.dbTypes) > 0 {
		options := make([]string, len(m.dbTypes))
		for i, opt := range m.dbTypes {
			if m.dbTypeCursor == i {
				options[i] = menuSelectedStyle.Render(opt.label)
			} else {
				options[i] = menuStyle.Render(opt.label)
			}
		}
		selector := fmt.Sprintf("Target Engine: %s", strings.Join(options, menuStyle.Render("  |  ")))
		s += dbSelectorLabelStyle.Render(selector) + "\n"
		hint := infoStyle.Render("Switch with <-/-> or t | Cluster backup requires PostgreSQL")
		s += hint + "\n"
	}

	// Database info
	dbInfo := infoStyle.Render(fmt.Sprintf("Database: %s@%s:%d (%s)",
		m.config.User, m.config.Host, m.config.Port, m.config.DisplayDatabaseType()))
	s += fmt.Sprintf("%s\n", dbInfo)

	// System resource profile badge
	if profileBadge := GetCompactProfileBadge(); profileBadge != "" {
		s += infoStyle.Render(fmt.Sprintf("System: %s", profileBadge)) + "\n"
	}
	s += "\n"

	// Menu items
	for i, choice := range m.choices {
		cursor := " "
		if m.cursor == i {
			cursor = ">"
			s += menuSelectedStyle.Render(fmt.Sprintf("%s %s", cursor, choice))
		} else {
			s += menuStyle.Render(fmt.Sprintf("%s %s", cursor, choice))
		}
		s += "\n"
	}

	// Message area
	if m.message != "" {
		s += "\n" + m.message + "\n"
	}

	// Footer
	footer := infoStyle.Render("\n[KEYS] Press Up/Down to navigate | Enter to select | q to quit")
	s += footer

	return s
}

// handleSingleBackup opens database selector for single backup
func (m *MenuModel) handleSingleBackup() (tea.Model, tea.Cmd) {
	selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "[SELECT] Single Database Backup", "single")
	return selector, selector.Init()
}

// handleSampleBackup opens database selector for sample backup
func (m *MenuModel) handleSampleBackup() (tea.Model, tea.Cmd) {
	selector := NewDatabaseSelector(m.config, m.logger, m, m.ctx, "[SELECT] Sample Database Backup", "sample")
	return selector, selector.Init()
}

// handleClusterBackup shows confirmation and executes cluster backup
func (m *MenuModel) handleClusterBackup() (tea.Model, tea.Cmd) {
	if !m.config.IsPostgreSQL() {
		m.message = errorStyle.Render("[FAIL] Cluster backup is available only for PostgreSQL targets")
		return m, nil
	}
	// Skip confirmation in auto-confirm mode
	if m.config.TUIAutoConfirm {
		executor := NewBackupExecution(m.config, m.logger, m, m.ctx, "cluster", "", 0)
		return executor, executor.Init()
	}
	confirm := NewConfirmationModelWithAction(m.config, m.logger, m,
		"[CHECK] Cluster Backup",
		"This will backup ALL databases in the cluster. Continue?",
		func() (tea.Model, tea.Cmd) {
			executor := NewBackupExecution(m.config, m.logger, m, m.ctx, "cluster", "", 0)
			return executor, executor.Init()
		})
	return confirm, confirm.Init()
}

// handleViewOperations shows active operations
func (m *MenuModel) handleViewOperations() (tea.Model, tea.Cmd) {
	ops := NewOperationsView(m.config, m.logger, m)
	return ops, nil
}

// handleOperationHistory shows operation history
func (m *MenuModel) handleOperationHistory() (tea.Model, tea.Cmd) {
	history := NewHistoryView(m.config, m.logger, m)
	return history, nil
}

// handleStatus shows database status
func (m *MenuModel) handleStatus() (tea.Model, tea.Cmd) {
	status := NewStatusView(m.config, m.logger, m)
	return status, status.Init()
}

// handleSettings opens settings
func (m *MenuModel) handleSettings() (tea.Model, tea.Cmd) {
	// Create and return the settings model
	settingsModel := NewSettingsModel(m.config, m.logger, m)
	return settingsModel, nil
}

// handleRestoreSingle opens archive browser for single restore
func (m *MenuModel) handleRestoreSingle() (tea.Model, tea.Cmd) {
	browser := NewArchiveBrowser(m.config, m.logger, m, m.ctx, "restore-single")
	return browser, browser.Init()
}

// handleRestoreCluster opens archive browser for cluster restore
func (m *MenuModel) handleRestoreCluster() (tea.Model, tea.Cmd) {
	if !m.config.IsPostgreSQL() {
		m.message = errorStyle.Render("[FAIL] Cluster restore is available only for PostgreSQL")
		return m, nil
	}
	browser := NewArchiveBrowser(m.config, m.logger, m, m.ctx, "restore-cluster")
	return browser, browser.Init()
}

// handleBackupManager opens backup management view
func (m *MenuModel) handleBackupManager() (tea.Model, tea.Cmd) {
	manager := NewBackupManager(m.config, m.logger, m, m.ctx)
	return manager, manager.Init()
}

// handleDiagnoseBackup opens archive browser for diagnosis
func (m *MenuModel) handleDiagnoseBackup() (tea.Model, tea.Cmd) {
	browser := NewArchiveBrowser(m.config, m.logger, m, m.ctx, "diagnose")
	return browser, browser.Init()
}

// handleSchedule shows backup schedule
func (m *MenuModel) handleSchedule() (tea.Model, tea.Cmd) {
	schedule := NewScheduleView(m.config, m.logger, m)
	return schedule, schedule.Init()
}

// handleChain shows backup chain
func (m *MenuModel) handleChain() (tea.Model, tea.Cmd) {
	chain := NewChainView(m.config, m.logger, m)
	return chain, chain.Init()
}

// handleTools opens the tools submenu
func (m *MenuModel) handleTools() (tea.Model, tea.Cmd) {
	tools := NewToolsMenu(m.config, m.logger, m, m.ctx)
	return tools, tools.Init()
}

// handleProfile opens the system resource profile view
func (m *MenuModel) handleProfile() (tea.Model, tea.Cmd) {
	profile := NewProfileModel(m.config, m.logger, m)
	return profile, profile.Init()
}

func (m *MenuModel) applyDatabaseSelection() {
	if m == nil || len(m.dbTypes) == 0 {
		return
	}
	if m.dbTypeCursor < 0 || m.dbTypeCursor >= len(m.dbTypes) {
		return
	}

	selection := m.dbTypes[m.dbTypeCursor]
	if err := m.config.SetDatabaseType(selection.value); err != nil {
		m.message = errorStyle.Render(fmt.Sprintf("[FAIL] %v", err))
		return
	}

	// Refresh default port if unchanged
	if m.config.Port == 0 {
		m.config.Port = m.config.GetDefaultPort()
	}

	m.message = successStyle.Render(fmt.Sprintf("[SWITCH] Target database set to %s", m.config.DisplayDatabaseType()))
	if m.logger != nil {
		m.logger.Info("updated target database type", "type", m.config.DatabaseType, "port", m.config.Port)
	}
}

// RunInteractiveMenu starts the simple TUI
func RunInteractiveMenu(cfg *config.Config, log logger.Logger) error {
	// CRITICAL: Add panic recovery to prevent crashes
	defer func() {
		if r := recover(); r != nil {
			if log != nil {
				log.Error("Interactive menu panic recovered", "panic", r)
			}
			fmt.Fprintf(os.Stderr, "\n[ERROR] Interactive menu crashed: %v\n", r)
			fmt.Fprintln(os.Stderr, "[INFO] Use CLI commands instead: dbbackup backup single <database>")
		}
	}()

	// Check for interactive terminal
	// Non-interactive terminals (screen backgrounded, pipes, etc.) cause scrambled output
	if !IsInteractiveTerminal() {
		if log != nil {
			log.Warn("TUI started in non-interactive terminal (screen/tmux backgrounded or piped output)",
				"hint", "Output may be scrambled. Consider using CLI commands instead.")
		}
		fmt.Fprintln(os.Stderr, "[WARN] Non-interactive terminal detected (screen/tmux backgrounded?)")
		fmt.Fprintln(os.Stderr, "[WARN] TUI output may be scrambled. Use CLI commands for background operations:")
		fmt.Fprintln(os.Stderr, "       dbbackup restore cluster <archive.tar.gz> --confirm")
	}

	m := NewMenuModel(cfg, log)
	p := tea.NewProgram(m)

	// Ensure cleanup on exit
	defer func() {
		if m != nil {
			m.Close()
		}
	}()

	if _, err := p.Run(); err != nil {
		return fmt.Errorf("error running interactive menu: %w", err)
	}

	return nil
}
