package tui

import (
	"fmt"
	"strings"

	"charm.land/bubbles/v2/textarea"
	"charm.land/bubbles/v2/textinput"
	tea "charm.land/bubbletea/v2"
	"charm.land/lipgloss/v2"
	"github.com/floatpane/matcha/config"
	"github.com/google/uuid"
)

var (
	suggestionStyle         = lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
	selectedSuggestionStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).Bold(true)
	suggestionBoxStyle      = lipgloss.NewStyle().Border(lipgloss.RoundedBorder()).BorderForeground(lipgloss.Color("240")).Padding(0, 1)
)

// Styles for the UI
var (
	focusedStyle        = lipgloss.NewStyle().Foreground(lipgloss.Color("42"))
	blurredStyle        = lipgloss.NewStyle().Foreground(lipgloss.Color("240"))
	noStyle             = lipgloss.NewStyle()
	helpStyle           = lipgloss.NewStyle().Foreground(lipgloss.Color("241"))
	focusedButton       = focusedStyle.Copy().Render("[ Send ]")
	blurredButton       = blurredStyle.Copy().Render("[ Send ]")
	emailRecipientStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).Bold(true)
	attachmentStyle     = lipgloss.NewStyle().PaddingLeft(4).Foreground(lipgloss.Color("240"))
	fromSelectorStyle   = lipgloss.NewStyle().Foreground(lipgloss.Color("42"))
)

const (
	focusFrom = iota
	focusTo
	focusCc
	focusBcc
	focusSubject
	focusBody
	focusSignature
	focusAttachment
	focusSend
)

// Composer model holds the state of the email composition UI.
type Composer struct {
	focusIndex     int
	toInput        textinput.Model
	ccInput        textinput.Model
	bccInput       textinput.Model
	subjectInput   textinput.Model
	bodyInput      textarea.Model
	signatureInput textarea.Model
	attachmentPath string
	width          int
	height         int
	confirmingExit bool

	// Multi-account support
	accounts           []config.Account
	selectedAccountIdx int
	showAccountPicker  bool

	// Contact suggestions
	suggestions        []config.Contact
	selectedSuggestion int
	showSuggestions    bool
	lastToValue        string

	// Draft persistence
	draftID string

	// Reply context
	inReplyTo  string
	references []string

	// Hidden quoted text (appended to body when sending, but not shown in editor)
	quotedText string
}

// NewComposer initializes a new composer model.
func NewComposer(from, to, subject, body string) *Composer {
	m := &Composer{
		draftID: uuid.New().String(),
	}

	m.toInput = textinput.New()
	m.toInput.Placeholder = "To"
	m.toInput.SetValue(to)
	m.toInput.Prompt = "> "
	m.toInput.CharLimit = 256

	m.ccInput = textinput.New()
	m.ccInput.Placeholder = "Cc"
	m.ccInput.Prompt = "> "
	m.ccInput.CharLimit = 256

	m.bccInput = textinput.New()
	m.bccInput.Placeholder = "Bcc"
	m.bccInput.Prompt = "> "
	m.bccInput.CharLimit = 256

	m.subjectInput = textinput.New()
	m.subjectInput.Placeholder = "Subject"
	m.subjectInput.SetValue(subject)
	m.subjectInput.Prompt = "> "
	m.subjectInput.CharLimit = 256

	m.bodyInput = textarea.New()
	m.bodyInput.Placeholder = "Body (Markdown supported)..."
	m.bodyInput.SetValue(body)
	m.bodyInput.Prompt = "> "
	m.bodyInput.SetHeight(10)

	m.signatureInput = textarea.New()
	m.signatureInput.Placeholder = "Signature (optional)..."
	m.signatureInput.Prompt = "> "
	m.signatureInput.SetHeight(3)
	// Load default signature
	if sig, err := config.LoadSignature(); err == nil && sig != "" {
		m.signatureInput.SetValue(sig)
	}

	// Start focus on To field (From is selectable but not a text input)
	m.focusIndex = focusTo
	m.toInput.Focus()

	return m
}

// NewComposerWithAccounts initializes a composer with multiple account support.
func NewComposerWithAccounts(accounts []config.Account, selectedAccountID string, to, subject, body string) *Composer {
	m := NewComposer("", to, subject, body)
	m.accounts = accounts

	// Find the selected account index
	for i, acc := range accounts {
		if acc.ID == selectedAccountID {
			m.selectedAccountIdx = i
			break
		}
	}

	return m
}

// ResetConfirmation ensures a restored draft isnt stuck in the exit prompt.
func (m *Composer) ResetConfirmation() {
	m.confirmingExit = false
}

func (m *Composer) Init() tea.Cmd {
	return textinput.Blink
}

func (m *Composer) getFromAddress() string {
	if len(m.accounts) > 0 && m.selectedAccountIdx < len(m.accounts) {
		acc := m.accounts[m.selectedAccountIdx]
		if acc.Name != "" {
			return fmt.Sprintf("%s <%s>", acc.Name, acc.FetchEmail)
		}
		return acc.FetchEmail
	}
	return ""
}

func (m *Composer) getSelectedAccount() *config.Account {
	if len(m.accounts) > 0 && m.selectedAccountIdx < len(m.accounts) {
		return &m.accounts[m.selectedAccountIdx]
	}
	return nil
}

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

	switch msg := msg.(type) {
	case tea.WindowSizeMsg:
		m.width = msg.Width
		m.height = msg.Height
		inputWidth := msg.Width - 6
		m.toInput.SetWidth(inputWidth)
		m.ccInput.SetWidth(inputWidth)
		m.bccInput.SetWidth(inputWidth)
		m.subjectInput.SetWidth(inputWidth)
		m.bodyInput.SetWidth(inputWidth)
		m.signatureInput.SetWidth(inputWidth)

	case FileSelectedMsg:
		m.attachmentPath = msg.Path
		return m, nil

	case tea.KeyPressMsg:
		// Handle contact suggestions mode
		if m.showSuggestions && len(m.suggestions) > 0 {
			switch msg.String() {
			case "up", "ctrl+p":
				if m.selectedSuggestion > 0 {
					m.selectedSuggestion--
				}
				return m, nil
			case "down", "ctrl+n":
				if m.selectedSuggestion < len(m.suggestions)-1 {
					m.selectedSuggestion++
				}
				return m, nil
			case "tab", "enter":
				// Select the suggestion
				selected := m.suggestions[m.selectedSuggestion]
				if selected.Name != "" && selected.Name != selected.Email {
					m.toInput.SetValue(fmt.Sprintf("%s <%s>", selected.Name, selected.Email))
				} else {
					m.toInput.SetValue(selected.Email)
				}
				m.lastToValue = m.toInput.Value()
				m.showSuggestions = false
				m.suggestions = nil
				return m, nil
			case "esc":
				m.showSuggestions = false
				m.suggestions = nil
				return m, nil
			}
			// For shift+tab, close suggestions and let it fall through to normal handling
			if msg.String() == "shift+tab" {
				m.showSuggestions = false
				m.suggestions = nil
			}
		}

		// Handle account picker mode
		if m.showAccountPicker {
			switch msg.String() {
			case "up", "k":
				if m.selectedAccountIdx > 0 {
					m.selectedAccountIdx--
				}
			case "down", "j":
				if m.selectedAccountIdx < len(m.accounts)-1 {
					m.selectedAccountIdx++
				}
			case "enter":
				m.showAccountPicker = false
			case "esc":
				m.showAccountPicker = false
			}
			return m, nil
		}

		if m.confirmingExit {
			switch msg.String() {
			case "y", "Y":
				return m, func() tea.Msg { return DiscardDraftMsg{ComposerState: m} }
			case "n", "N", "esc":
				m.confirmingExit = false
				return m, nil
			default:
				return m, nil
			}
		}

		switch msg.String() {
		case "ctrl+c":
			return m, tea.Quit
		case "esc":
			m.confirmingExit = true
			return m, nil

		case "tab", "shift+tab":
			if msg.String() == "shift+tab" {
				m.focusIndex--
			} else {
				m.focusIndex++
			}

			maxFocus := focusSend
			minFocus := focusFrom
			// Skip From field if only one account (nothing to switch)
			if len(m.accounts) <= 1 {
				minFocus = focusTo
			}

			if m.focusIndex > maxFocus {
				m.focusIndex = minFocus
			} else if m.focusIndex < minFocus {
				m.focusIndex = maxFocus
			}

			m.toInput.Blur()
			m.ccInput.Blur()
			m.bccInput.Blur()
			m.subjectInput.Blur()
			m.bodyInput.Blur()
			m.signatureInput.Blur()

			switch m.focusIndex {
			case focusTo:
				cmds = append(cmds, m.toInput.Focus())
			case focusCc:
				cmds = append(cmds, m.ccInput.Focus())
			case focusBcc:
				cmds = append(cmds, m.bccInput.Focus())
			case focusSubject:
				cmds = append(cmds, m.subjectInput.Focus())
			case focusBody:
				cmds = append(cmds, m.bodyInput.Focus())
			case focusSignature:
				cmds = append(cmds, m.signatureInput.Focus())
			}
			return m, tea.Batch(cmds...)

		case "enter":
			switch m.focusIndex {
			case focusFrom:
				if len(m.accounts) > 1 {
					m.showAccountPicker = true
				}
				return m, nil
			case focusAttachment:
				return m, func() tea.Msg { return GoToFilePickerMsg{} }
			case focusSend:
				acc := m.getSelectedAccount()
				accountID := ""
				if acc != nil {
					accountID = acc.ID
				}
				return m, func() tea.Msg {
					return SendEmailMsg{
						To:             m.toInput.Value(),
						Cc:             m.ccInput.Value(),
						Bcc:            m.bccInput.Value(),
						Subject:        m.subjectInput.Value(),
						Body:           m.bodyInput.Value(),
						AttachmentPath: m.attachmentPath,
						AccountID:      accountID,
						QuotedText:     m.quotedText,
						InReplyTo:      m.inReplyTo,
						References:     m.references,
						Signature:      m.signatureInput.Value(),
					}
				}
			}
		}
	}

	switch m.focusIndex {
	case focusTo:
		m.toInput, cmd = m.toInput.Update(msg)
		cmds = append(cmds, cmd)

		// Check if To field value changed and update suggestions
		currentValue := m.toInput.Value()
		if currentValue != m.lastToValue {
			m.lastToValue = currentValue
			if len(currentValue) >= 2 {
				m.suggestions = config.SearchContacts(currentValue)
				m.showSuggestions = len(m.suggestions) > 0
				m.selectedSuggestion = 0
			} else {
				m.showSuggestions = false
				m.suggestions = nil
			}
		}
	case focusCc:
		m.ccInput, cmd = m.ccInput.Update(msg)
		cmds = append(cmds, cmd)
	case focusBcc:
		m.bccInput, cmd = m.bccInput.Update(msg)
		cmds = append(cmds, cmd)
	case focusSubject:
		m.subjectInput, cmd = m.subjectInput.Update(msg)
		cmds = append(cmds, cmd)
	case focusBody:
		m.bodyInput, cmd = m.bodyInput.Update(msg)
		cmds = append(cmds, cmd)
	case focusSignature:
		m.signatureInput, cmd = m.signatureInput.Update(msg)
		cmds = append(cmds, cmd)
	}

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

func (m *Composer) View() tea.View {
	var composerView strings.Builder
	var button string

	if m.focusIndex == focusSend {
		button = focusedButton
	} else {
		button = blurredButton
	}

	// From field with account selector
	fromAddr := m.getFromAddress()
	var fromField string
	if len(m.accounts) > 1 {
		if m.focusIndex == focusFrom {
			fromField = focusedStyle.Render(fmt.Sprintf("> From: %s [Enter to switch]", fromAddr))
		} else {
			fromField = blurredStyle.Render(fmt.Sprintf("  From: %s [switchable]", fromAddr))
		}
	} else if fromAddr != "" {
		fromField = "  From: " + emailRecipientStyle.Render(fromAddr)
	} else {
		fromField = blurredStyle.Render("  From: (no account configured)")
	}

	var attachmentField string
	attachmentText := "None (Press Enter to select)"
	if m.attachmentPath != "" {
		attachmentText = m.attachmentPath
	}

	if m.focusIndex == focusAttachment {
		attachmentField = focusedStyle.Render(fmt.Sprintf("> Attachment: %s", attachmentText))
	} else {
		attachmentField = blurredStyle.Render(fmt.Sprintf("  Attachment: %s", attachmentText))
	}

	// Build To field with suggestions
	toFieldView := m.toInput.View()
	if m.showSuggestions && len(m.suggestions) > 0 {
		var suggestionsBuilder strings.Builder
		for i, s := range m.suggestions {
			display := s.Email
			if s.Name != "" && s.Name != s.Email {
				display = fmt.Sprintf("%s <%s>", s.Name, s.Email)
			}
			if i == m.selectedSuggestion {
				suggestionsBuilder.WriteString(selectedSuggestionStyle.Render("> "+display) + "\n")
			} else {
				suggestionsBuilder.WriteString(suggestionStyle.Render("  "+display) + "\n")
			}
		}
		toFieldView = toFieldView + "\n" + suggestionBoxStyle.Render(strings.TrimSuffix(suggestionsBuilder.String(), "\n"))
	}

	// Signature field label
	var signatureLabel string
	if m.focusIndex == focusSignature {
		signatureLabel = focusedStyle.Render("Signature:")
	} else {
		signatureLabel = blurredStyle.Render("Signature:")
	}

	composerView.WriteString(lipgloss.JoinVertical(lipgloss.Left,
		"Compose New Email",
		fromField,
		toFieldView,
		m.ccInput.View(),
		m.bccInput.View(),
		m.subjectInput.View(),
		m.bodyInput.View(),
		signatureLabel,
		m.signatureInput.View(),
		attachmentStyle.Render(attachmentField),
		button,
		helpStyle.Render("Markdown/HTML • tab/shift+tab: navigate • esc: save draft & exit"),
	))

	// Account picker overlay
	if m.showAccountPicker {
		var accountList strings.Builder
		accountList.WriteString("Select Account:\n\n")
		for i, acc := range m.accounts {
			display := acc.FetchEmail
			if acc.Name != "" {
				display = fmt.Sprintf("%s (%s)", acc.Name, acc.FetchEmail)
			}
			if i == m.selectedAccountIdx {
				accountList.WriteString(selectedItemStyle.Render(fmt.Sprintf("> %s", display)))
			} else {
				accountList.WriteString(itemStyle.Render(fmt.Sprintf("  %s", display)))
			}
			accountList.WriteString("\n")
		}
		accountList.WriteString("\n")
		accountList.WriteString(HelpStyle.Render("↑/↓: navigate • enter: select • esc: cancel"))

		dialog := DialogBoxStyle.Render(accountList.String())
		return tea.NewView(lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, dialog))
	}

	if m.confirmingExit {
		dialog := DialogBoxStyle.Render(
			lipgloss.JoinVertical(lipgloss.Center,
				"Discard draft?",
				HelpStyle.Render("\n(y/n)"),
			),
		)
		return tea.NewView(lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, dialog))
	}

	return tea.NewView(composerView.String())
}

// SetAccounts sets the available accounts for sending.
func (m *Composer) SetAccounts(accounts []config.Account) {
	m.accounts = accounts
	if m.selectedAccountIdx >= len(accounts) {
		m.selectedAccountIdx = 0
	}
}

// SetSelectedAccount sets the selected account by ID.
func (m *Composer) SetSelectedAccount(accountID string) {
	for i, acc := range m.accounts {
		if acc.ID == accountID {
			m.selectedAccountIdx = i
			return
		}
	}
}

// GetSelectedAccountID returns the ID of the currently selected account.
func (m *Composer) GetSelectedAccountID() string {
	if len(m.accounts) > 0 && m.selectedAccountIdx < len(m.accounts) {
		return m.accounts[m.selectedAccountIdx].ID
	}
	return ""
}

// GetDraftID returns the draft ID for this composer.
func (m *Composer) GetDraftID() string {
	return m.draftID
}

// SetDraftID sets the draft ID (for loading existing drafts).
func (m *Composer) SetDraftID(id string) {
	m.draftID = id
}

// GetTo returns the current To field value.
func (m *Composer) GetTo() string {
	return m.toInput.Value()
}

// GetSubject returns the current Subject field value.
func (m *Composer) GetSubject() string {
	return m.subjectInput.Value()
}

// GetBody returns the current Body field value.
func (m *Composer) GetBody() string {
	return m.bodyInput.Value()
}

// GetAttachmentPath returns the current attachment path.
func (m *Composer) GetAttachmentPath() string {
	return m.attachmentPath
}

// GetSignature returns the current signature value.
func (m *Composer) GetSignature() string {
	return m.signatureInput.Value()
}

// SetReplyContext sets the reply context for the draft.
func (m *Composer) SetReplyContext(inReplyTo string, references []string) {
	m.inReplyTo = inReplyTo
	m.references = references
}

// SetQuotedText sets the hidden quoted text that will be appended when sending.
func (m *Composer) SetQuotedText(text string) {
	m.quotedText = text
}

// GetQuotedText returns the hidden quoted text.
func (m *Composer) GetQuotedText() string {
	return m.quotedText
}

// GetInReplyTo returns the In-Reply-To header value.
func (m *Composer) GetInReplyTo() string {
	return m.inReplyTo
}

// GetReferences returns the References header values.
func (m *Composer) GetReferences() []string {
	return m.references
}

// ToDraft converts the composer state to a Draft for saving.
func (m *Composer) ToDraft() config.Draft {
	return config.Draft{
		ID:             m.draftID,
		To:             m.toInput.Value(),
		Cc:             m.ccInput.Value(),
		Bcc:            m.bccInput.Value(),
		Subject:        m.subjectInput.Value(),
		Body:           m.bodyInput.Value(),
		AttachmentPath: m.attachmentPath,
		AccountID:      m.GetSelectedAccountID(),
		InReplyTo:      m.inReplyTo,
		References:     m.references,
		QuotedText:     m.quotedText,
	}
}

// NewComposerFromDraft creates a composer from an existing draft.
func NewComposerFromDraft(draft config.Draft, accounts []config.Account) *Composer {
	m := NewComposerWithAccounts(accounts, draft.AccountID, draft.To, draft.Subject, draft.Body)
	m.ccInput.SetValue(draft.Cc)
	m.bccInput.SetValue(draft.Bcc)
	m.draftID = draft.ID
	m.attachmentPath = draft.AttachmentPath
	m.inReplyTo = draft.InReplyTo
	m.references = draft.References
	m.quotedText = draft.QuotedText
	return m
}
