package replay

import (
	"fmt"

	"charm.land/bubbles/v2/help"
	"charm.land/bubbles/v2/key"
	"charm.land/bubbles/v2/paginator"
	"charm.land/bubbles/v2/textarea"
	"charm.land/bubbles/v2/viewport"
	tea "charm.land/bubbletea/v2"
	"github.com/anotherhadi/spilltea/internal/db"
	"github.com/anotherhadi/spilltea/internal/keys"
	"github.com/anotherhadi/spilltea/internal/style"
)

type SendToReplayMsg struct {
	Scheme     string
	Host       string
	RequestRaw string
}

type Entry struct {
	DBID        int64
	Scheme      string
	Host        string
	Path        string
	Method      string
	OriginalRaw string
	RequestRaw  string // current (possibly edited) request
	ResponseRaw string // filled after send
	StatusCode  int    // 0 = not sent yet
	Sending     bool
	Err         error
}

type Model struct {
	entries  []Entry
	cursor   int
	editing  bool
	database *db.DB

	listViewport     viewport.Model
	requestViewport  viewport.Model
	responseViewport viewport.Model
	textarea         textarea.Model
	pager            paginator.Model
	help             help.Model

	width  int
	height int
}

func New() Model {
	ta := style.NewTextarea(false)
	ta.Blur()
	return Model{
		listViewport:     style.NewViewport(),
		requestViewport:  style.NewViewport(),
		responseViewport: style.NewViewport(),
		textarea:         ta,
		pager:            style.NewPaginator(),
		help:             style.NewHelp(),
	}
}

func (m Model) Init() tea.Cmd { return nil }

func (m Model) IsEditing() bool { return m.editing }

func (m Model) CurrentRaw() string {
	if len(m.entries) == 0 || m.cursor >= len(m.entries) {
		return ""
	}
	return m.entries[m.cursor].RequestRaw
}

func (m Model) CurrentScheme() string {
	if len(m.entries) == 0 || m.cursor >= len(m.entries) {
		return "https"
	}
	if s := m.entries[m.cursor].Scheme; s != "" {
		return s
	}
	return "https"
}

func (m *Model) SetDB(d *db.DB) {
	m.database = d
	if d == nil {
		return
	}
	entries, err := d.ListReplayEntries()
	if err != nil {
		return
	}
	for _, dbe := range entries {
		m.entries = append(m.entries, entryFromDB(dbe))
	}
	m.pager.SetTotalPages(len(m.entries))
	if len(m.entries) > 0 {
		m.cursor = len(m.entries) - 1
	}
	m.refreshListViewport()
	m.refreshBody()
}

func entryFromDB(dbe db.ReplayEntry) Entry {
	var err error
	if dbe.ErrorMsg != "" {
		err = fmt.Errorf("%s", dbe.ErrorMsg)
	}
	return Entry{
		DBID:        dbe.ID,
		Scheme:      dbe.Scheme,
		Host:        dbe.Host,
		Path:        dbe.Path,
		Method:      dbe.Method,
		OriginalRaw: dbe.OriginalRaw,
		RequestRaw:  dbe.RequestRaw,
		ResponseRaw: dbe.ResponseRaw,
		StatusCode:  dbe.StatusCode,
		Err:         err,
	}
}

func (m *Model) SetSize(w, h int) {
	m.width = w
	m.height = h
	m.recalcSizes()
}

func (m *Model) recalcSizes() {
	m.help.SetWidth(m.width - 2)

	listH, bodyH := style.SplitH(m.height, m.renderStatusBar(), 0.35)

	listInner := m.width - 2
	if listInner < 0 {
		listInner = 0
	}
	listVH := style.PanelContentH(listH) - 1 // -1 for the pager dots row
	if listVH < 0 {
		listVH = 0
	}
	m.listViewport.SetWidth(listInner)
	m.listViewport.SetHeight(listVH)
	m.pager.PerPage = listVH
	if m.pager.PerPage < 1 {
		m.pager.PerPage = 1
	}

	leftW, rightW := m.bodyHalfWidths()
	leftInner := leftW - 2
	rightInner := rightW - 2
	if leftInner < 0 {
		leftInner = 0
	}
	if rightInner < 0 {
		rightInner = 0
	}
	bodyVH := style.PanelContentH(bodyH)

	m.requestViewport.SetWidth(leftInner)
	m.requestViewport.SetHeight(bodyVH)
	m.responseViewport.SetWidth(rightInner)
	m.responseViewport.SetHeight(bodyVH)
	m.textarea.SetWidth(leftInner)
	m.textarea.SetHeight(bodyVH)

	m.refreshListViewport()
	m.refreshBody()
}

func (m *Model) bodyHalfWidths() (left, right int) {
	left = m.width / 2
	right = m.width - left
	return
}

type replayKeyMap struct{ width int }

func (replayKeyMap) ShortHelp() []key.Binding {
	g := keys.Keys.Global
	r := keys.Keys.Replay
	return []key.Binding{g.Up, g.Down, r.Send, r.Edit, g.Help}
}

func (m replayKeyMap) FullHelp() [][]key.Binding {
	g := keys.Keys.Global
	pageGlobals := []key.Binding{g.Up, g.Down, g.ScrollUp, g.ScrollDown, g.Left, g.Right, g.Escape, g.Copy, g.CopyAs}
	all := append(keys.Keys.Replay.Bindings(), pageGlobals...)
	all = append(all, g.CommonBindings()...)
	return keys.ChunkByWidth(all, m.width)
}
