package ui

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	"github.com/charmbracelet/bubbles/list"
	"github.com/charmbracelet/bubbles/spinner"
	"github.com/charmbracelet/bubbles/textinput"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"

	"github.com/your-username/todo/src/ai"
	"github.com/your-username/todo/src/database"
	"github.com/your-username/todo/src/models"
)

var (
	titleStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("62"))

	completedStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("42")) // Green color

	taskStyle = lipgloss.NewStyle().
			BorderStyle(lipgloss.NormalBorder()).
			BorderForeground(lipgloss.Color("240")).
			Padding(1, 2)

	statusStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("34")) // Blue color

	dimmedStyle = lipgloss.NewStyle().
			Foreground(lipgloss.Color("240")) // Dimmed gray color
)

type Model struct {
	list            list.Model // Changed from *list.Model to list.Model
	titleInput      textinput.Model
	bodyInput       textinput.Model
	searchInput     textinput.Model
	spinner         spinner.Model
	db              database.Database
	aiService       *ai.AIService
	state           State
	selected        *database.Task
	errorMsg        string
	filter          string
	currentPage     int
	itemsPerPage    int
	showCompleted   bool
	analysisResults map[int64]*database.Analysis
	isLoading       bool // Track loading state
	analyzingTasks  map[int]bool
	analysisTimeout time.Duration
}

type State int

const (
	StateNormal State = iota
	StateCreating
	StateEditing
	StateSearching
	StateViewing
	StateAnalyzing
)

func New(db database.Database, aiService *ai.AIService) Model {
	ti := textinput.New()
	ti.Placeholder = "Task title"
	ti.Focus()

	bi := textinput.New()
	bi.Placeholder = "Task description (optional)"

	si := textinput.New()
	si.Placeholder = "Search tasks..."

	delegate := list.NewDefaultDelegate()
	delegate.Styles.SelectedTitle = delegate.Styles.SelectedTitle.Foreground(lipgloss.Color("62"))
	delegate.Styles.SelectedDesc = delegate.Styles.SelectedDesc.Foreground(lipgloss.Color("241"))

	taskList := list.New(nil, delegate, 0, 0)
	taskList.Title = "Todo Tasks"
	taskList.SetShowTitle(true)
	taskList.SetFilteringEnabled(true)

	s := spinner.New()
	s.Spinner = spinner.Dot
	s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))

	m := Model{
		list:            taskList,
		titleInput:      ti,
		bodyInput:       bi,
		searchInput:     si,
		spinner:         s,
		db:              db,
		aiService:       aiService,
		state:           StateNormal,
		itemsPerPage:    10,
		analysisResults: make(map[int64]*database.Analysis),
		isLoading:       true,
		analyzingTasks:  make(map[int]bool),
		showCompleted:   true, // Show all tasks by default
		analysisTimeout: 5 * time.Second,
	}

	return m
}

func (m Model) Init() tea.Cmd {
	m.isLoading = true // Set initial loading state
	return tea.Batch(m.loadTasks, m.spinner.Tick)
}

func (m Model) loadTasks() tea.Msg {
	dbTasks, err := m.db.GetTasks(context.Background(), m.showCompleted)
	if err != nil {
		return errMsg{err}
	}

	// Convert database.Task to models.Task
	var items []list.Item
	for _, t := range dbTasks {
		items = append(items, taskItem{
			task: models.Task{
				ID:        t.ID,
				Title:     t.Title,
				Completed: t.Completed,
				CreatedAt: t.CreatedAt,
			},
			analyzing: m.analyzingTasks[int(t.ID)],
		})
	}

	return tasksLoadedMsg{items: items}
}

func (m Model) updateTaskList(tasks []models.Task) {
	items := make([]list.Item, len(tasks))
	for i, task := range tasks {
		items[i] = taskItem{
			task:      task,
			analyzing: m.analyzingTasks[int(task.ID)],
		}
	}
	m.list.SetItems(items)
}

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

	switch msg := msg.(type) {
	case nil:
		// Initial update, load tasks
		m.isLoading = true
		return m, m.loadTasks

	case spinner.TickMsg:
		var cmd tea.Cmd
		m.spinner, cmd = m.spinner.Update(msg)
		return m, cmd

	case tasksLoadedMsg:
		m.isLoading = false
		m.list.SetItems(msg.items)
		if len(msg.items) > 0 {
			m.list.Select(0)
		}
		return m, nil

	case tea.KeyMsg:
		switch msg.String() {
		case "q", "ctrl+c":
			return m, tea.Quit
		case "n":
			if m.state == StateNormal {
				m.state = StateCreating
				m.titleInput.Focus()
				return m, textinput.Blink
			}
		case "/":
			if m.state == StateNormal {
				m.state = StateSearching
				m.searchInput.Focus()
				return m, textinput.Blink
			}
		case "e":
			if m.state == StateNormal {
				if i, ok := m.list.SelectedItem().(taskItem); ok {
					m.state = StateEditing
					m.selected = &database.Task{ID: i.task.ID, Title: i.task.Title, Completed: i.task.Completed}
					m.titleInput.SetValue(i.task.Title)
					m.titleInput.Focus()
					return m, textinput.Blink
				}
			}
		case "tab":
			if m.state == StateCreating || m.state == StateEditing {
				if m.titleInput.Focused() {
					m.titleInput.Blur()
					m.bodyInput.Focus()
					return m, textinput.Blink
				} else if m.bodyInput.Focused() {
					m.bodyInput.Blur()
					m.titleInput.Focus()
					return m, textinput.Blink
				}
			}
			m.showCompleted = !m.showCompleted
			m.filter = "" // Clear filter when toggling completed tasks
			m.isLoading = true
			return m, m.loadTasks
		case "shift+tab":
			if m.state == StateCreating || m.state == StateEditing {
				if m.titleInput.Focused() {
					m.titleInput.Blur()
					m.bodyInput.Focus()
					return m, textinput.Blink
				} else if m.bodyInput.Focused() {
					m.bodyInput.Blur()
					m.titleInput.Focus()
					return m, textinput.Blink
				}
			}
		case "c":
			if m.state == StateNormal {
				if i, ok := m.list.SelectedItem().(taskItem); ok {
					err := m.db.CompleteTask(context.Background(), int(i.task.ID))
					if err != nil {
						m.errorMsg = fmt.Sprintf("Failed to complete task: %v", err)
					}
					m.showCompleted = true // Show completed tasks after completing one
					m.filter = ""          // Clear filter when showing completed tasks
					m.isLoading = true
					return m, m.loadTasks
				}
			}
		case "a":
			if m.state == StateNormal || m.state == StateViewing {
				if i, ok := m.list.SelectedItem().(taskItem); ok {
					m.state = StateAnalyzing
					m.selected = &database.Task{ID: i.task.ID, Title: i.task.Title, Completed: i.task.Completed}
					m.analyzingTasks[int(i.task.ID)] = true
					return m, m.analyzeTask(i.task)
				}
			}
			return m, nil
		case "enter":
			switch m.state {
			case StateCreating:
				if m.titleInput.Focused() && m.titleInput.Value() != "" {
					m.titleInput.Blur()
					m.bodyInput.Focus()
					return m, textinput.Blink
				} else if m.bodyInput.Focused() {
					if m.titleInput.Value() != "" {
						_, err := m.db.AddTask(context.Background(), m.titleInput.Value(), m.bodyInput.Value())
						if err != nil {
							m.errorMsg = fmt.Sprintf("Failed to add task: %v", err)
						}
						m.titleInput.Reset()
						m.bodyInput.Reset()
						m.state = StateNormal
						m.isLoading = true
						return m, m.loadTasks
					}
				}
			case StateEditing:
				if m.titleInput.Focused() && m.titleInput.Value() != "" {
					m.titleInput.Blur()
					m.bodyInput.Focus()
					return m, textinput.Blink
				} else if m.bodyInput.Focused() && m.selected != nil {
					err := m.db.UpdateTask(context.Background(), m.selected.ID, m.titleInput.Value(), m.bodyInput.Value())
					if err != nil {
						m.errorMsg = fmt.Sprintf("Failed to update task: %v", err)
					}
					m.titleInput.Reset()
					m.bodyInput.Reset()
					m.selected = nil
					m.state = StateNormal
					m.isLoading = true
					return m, m.loadTasks
				}
			case StateSearching:
				m.filter = m.searchInput.Value()
				m.searchInput.Reset()
				m.state = StateNormal
				m.isLoading = true
				return m, m.loadTasks
			case StateNormal:
				if i, ok := m.list.SelectedItem().(taskItem); ok {
					m.selected = &database.Task{ID: i.task.ID, Title: i.task.Title, Completed: i.task.Completed}
					m.state = StateViewing

					// Load existing analysis when viewing task
					analysis, err := m.db.GetTaskAnalysis(context.Background(), int(i.task.ID))
					if err == nil && analysis != nil {
						if m.analysisResults == nil {
							m.analysisResults = make(map[int64]*database.Analysis)
						}
						m.analysisResults[i.task.ID] = analysis
						return m, nil
					}

					// If no analysis exists, run the analysis
					return m, m.analyzeTask(i.task)
				}
			}
		case "esc":
			if m.state != StateNormal {
				m.state = StateNormal
				m.errorMsg = ""
				m.selected = nil
				m.titleInput.Reset()
				m.bodyInput.Reset()
				if m.state == StateSearching {
					m.filter = ""
				}
				m.isLoading = true
				return m, m.loadTasks
			}
		}

	case tea.WindowSizeMsg:
		m.list.SetWidth(msg.Width)
		m.list.SetHeight(msg.Height)
		return m, nil

	case analysisMsg:
		if m.state == StateAnalyzing {
			// Get the currently selected task
			if i, ok := m.list.SelectedItem().(taskItem); ok {
				// Create clean, properly formatted JSON
				analysis := &database.Analysis{
					TaskID: i.task.ID,
					Result: fmt.Sprintf(`{"priority":%d,"category":"%s","estimate":%d,"related":"%s","summary":"%s"}`,
						msg.result.Priority,
						msg.result.Category,
						msg.result.TimeEstimate,
						msg.result.RelatedTasks,
						msg.result.Summary),
					AnalyzedAt: time.Now(),
				}

				if err := m.db.StoreAnalysis(context.Background(), analysis); err != nil {
					m.errorMsg = fmt.Sprintf("Failed to store analysis: %v", err)
				} else {
					if m.analysisResults == nil {
						m.analysisResults = make(map[int64]*database.Analysis)
					}
					m.analysisResults[i.task.ID] = analysis
				}
				delete(m.analyzingTasks, int(i.task.ID))
			}
			m.state = StateViewing
			return m, nil
		}

	case errMsg:
		m.errorMsg = msg.Error()
		return m, nil
	}

	// Update the appropriate input based on current state
	var cmd tea.Cmd
	switch m.state {
	case StateAnalyzing:
		var spinnerCmd tea.Cmd
		m.spinner, spinnerCmd = m.spinner.Update(msg)
		cmds = append(cmds, spinnerCmd)
	case StateCreating, StateEditing:
		m.titleInput, cmd = m.titleInput.Update(msg)
		cmds = append(cmds, cmd)
		m.bodyInput, cmd = m.bodyInput.Update(msg)
		cmds = append(cmds, cmd)
	case StateSearching:
		m.searchInput, cmd = m.searchInput.Update(msg)
		cmds = append(cmds, cmd)
	case StateNormal:
		if !m.isLoading {
			m.list, cmd = m.list.Update(msg)
			cmds = append(cmds, cmd)
		}
		var spinnerCmd tea.Cmd
		m.spinner, spinnerCmd = m.spinner.Update(msg)
		cmds = append(cmds, spinnerCmd)
	}

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

func (m Model) View() string {
	var b strings.Builder

	if m.errorMsg != "" {
		b.WriteString(fmt.Sprintf("Error: %s\n\n", m.errorMsg))
	}

	switch m.state {
	case StateCreating:
		b.WriteString("Add new task:\n\n")
		b.WriteString("Title:\n")
		b.WriteString(m.titleInput.View())
		b.WriteString("\n\nDescription:\n")
		b.WriteString(m.bodyInput.View())
		b.WriteString("\n\n(Tab/Shift+Tab to switch fields, Enter to move to description or save, ESC to cancel)")

	case StateEditing:
		b.WriteString("Edit task:\n\n")
		b.WriteString("Title:\n")
		b.WriteString(m.titleInput.View())
		b.WriteString("\n\nDescription:\n")
		b.WriteString(m.bodyInput.View())
		b.WriteString("\n\n(Tab/Shift+Tab to switch fields, Enter to move to description or save, ESC to cancel)")

	case StateSearching:
		b.WriteString("Search tasks:\n\n")
		b.WriteString(m.searchInput.View())
		b.WriteString("\n\n(ESC to cancel)")

	case StateViewing:
		if m.selected != nil {
			b.WriteString(m.renderTaskDetail())
			return b.String()
		}

	case StateAnalyzing:
		b.WriteString(fmt.Sprintf("\n\n%s Analyzing task...\n", m.spinner.View()))
		return taskStyle.Render(b.String())

	default:
		header := fmt.Sprintf(
			"%s\n\nPress 'n' for new task, 'e' to edit task, '/' to search, TAB to toggle completed, 'c' to complete task\n",
			titleStyle.Render("Your Tasks"),
		)
		b.WriteString(header)
		if m.isLoading {
			b.WriteString(fmt.Sprintf("\n%s Loading tasks...\n", m.spinner.View()))
		} else {
			b.WriteString(m.list.View())
		}
	}

	return b.String()
}

func (m Model) renderTaskDetail() string {
	var b strings.Builder

	if m.selected == nil {
		return ""
	}

	title := m.selected.Title
	if m.selected.Completed {
		title = completedStyle.Render(title)
	}

	b.WriteString(fmt.Sprintf("Title: %s\n\n", title))

	// Add analysis results if available
	if analysis, ok := m.analysisResults[m.selected.ID]; ok {
		var result ai.AnalysisResult
		if err := json.Unmarshal([]byte(analysis.Result), &result); err == nil {
			b.WriteString("\nAnalysis Results:\n")
			b.WriteString(fmt.Sprintf("Priority: %d/5\n", result.Priority))
			b.WriteString(fmt.Sprintf("Category: %s\n", result.Category))
			b.WriteString(fmt.Sprintf("Time Estimate: %d minutes\n", result.TimeEstimate))
			if result.RelatedTasks != "" {
				b.WriteString(fmt.Sprintf("Related Tasks: %s\n", result.RelatedTasks))
			}
			if result.Summary != "" && result.Summary != title {
				b.WriteString(fmt.Sprintf("Summary: %s\n", result.Summary))
			}
			b.WriteString(fmt.Sprintf("\nAnalyzed at: %s", analysis.AnalyzedAt.Format("2006-01-02 15:04:05")))
		}
	}

	b.WriteString("\n\nPress 'a' to analyze task, ESC to return")
	return taskStyle.Render(b.String())
}

func (m Model) analyzeTask(task models.Task) tea.Cmd {
	return func() tea.Msg {
		// Create a timeout channel
		done := make(chan struct{})
		var result *ai.AnalysisResult
		var err error

		// Run analysis in a goroutine
		go func() {
			result, err = m.aiService.AnalyzeTask(context.Background(), task.Title)
			close(done)
		}()

		// Wait for either completion or timeout
		select {
		case <-done:
			if err != nil {
				return errMsg{err}
			}
			return analysisMsg{result}
		case <-time.After(m.analysisTimeout):
			return analysisMsg{&ai.AnalysisResult{
				Priority:     3,
				Category:     "task",
				TimeEstimate: 30,
				RelatedTasks: "",
				Summary:      "Analysis timed out for: " + task.Title,
			}}
		}
	}
}

type taskItem struct {
	task      models.Task
	analyzing bool
}

func (i taskItem) Title() string {
	status := "[ ]"
	if i.task.Completed {
		status = "[x]"
	}

	aiStatus := ""
	if i.analyzing {
		aiStatus = " (analyzing...)"
	}

	return fmt.Sprintf("%s %s%s", status, i.task.Title, aiStatus)
}

func (i taskItem) Description() string { return "" }
func (i taskItem) FilterValue() string { return i.task.Title }

type tasksLoadedMsg struct {
	items []list.Item
}

type analysisMsg struct {
	result *ai.AnalysisResult
}

type errMsg struct{ error }
