"""Filterable local branch picker."""

from __future__ import annotations

from pyratatui import (
    Constraint,
    Direction,
    Frame,
    Layout,
    List,
    ListItem,
    ListState,
    Paragraph,
    Rect,
    TextPrompt,
    TextState,
)

from pygittools.tui.branches import (
    checkout_branch,
    create_branch_from_current,
    current_branch_name,
    list_local_branches,
)
from pygittools.tui.git_colors import current_theme, focus_style
from pygittools.tui.keys import KeyLike, is_down, is_enter, is_esc, is_quit, is_up, move_hint
from pygittools.tui.types import PageAction, PageContext, PageResult


class BranchPickerPage:
    title = "branches"

    def __init__(self) -> None:
        self._ctx: PageContext | None = None
        self._filter_state = TextState()
        self._filter_state.focus()
        self._branches: list[str] = []
        self._filtered: list[str] = []
        self._list_state = ListState()
        self._focus_filter = True
        self._error: str | None = None

    def on_enter(self, ctx: PageContext) -> None:
        self._ctx = ctx
        self._filter_state.set_value("")
        self._filter_state.focus()
        self._focus_filter = True
        self._error = None
        self._list_state.select(0)
        self._reload_branches()

    def status_text(self) -> str:
        if self._error:
            return self._error
        if not self._filtered:
            if self._filter_state.value().strip():
                return "Enter create branch — type name — Esc back"
            return "No matching branches — type to filter, Esc back"
        return f"Enter checkout — type to filter — {move_hint()} — Esc back"

    def draw(self, frame: Frame, area: Rect) -> None:
        if area.height <= 0:
            return
        theme = current_theme()
        chunks = (
            Layout()
            .direction(Direction.Vertical)
            .constraints([Constraint.length(1), Constraint.length(1), Constraint.fill(1)])
            .split(area)
        )
        frame.render_widget(Paragraph.from_string("Switch branch").style(theme.header), chunks[0])
        if self._focus_filter:
            self._filter_state.focus()
        else:
            self._filter_state.blur()
        frame.render_text_prompt(TextPrompt("Filter: "), chunks[1], self._filter_state)

        current = current_branch_name(self._ctx.repo) if self._ctx is not None else ""
        items: list[ListItem] = []
        selected = self._list_state.selected or 0
        for index, branch in enumerate(self._filtered):
            marker = "* " if branch == current else "  "
            focused = not self._focus_filter and index == selected
            style = focus_style(theme.normal) if focused else theme.normal
            items.append(ListItem(f"{marker}{branch}", style))
        if not items:
            items.append(ListItem("(no matches)", theme.log_date))
        frame.render_stateful_list(
            List(items).highlight_style(theme.focus).highlight_symbol(""),
            chunks[2],
            self._list_state,
        )

    def handle_key(self, key: KeyLike) -> PageResult:
        if is_esc(key) or (not self._focus_filter and is_quit(key)):
            return PageResult(action=PageAction.POP)
        if self._error is not None:
            self._error = None

        ctx = self._ctx
        if ctx is None:
            return PageResult()

        if self._focus_filter:
            if is_down(key) and self._filtered:
                self._focus_filter = False
                self._list_state.select(0)
                return PageResult()
            if is_enter(key):
                if not self._filtered:
                    return self._try_create_branch(ctx)
                self._focus_filter = False
                self._list_state.select(0)
                return PageResult()
            self._filter_state.focus()
            self._filter_state.handle_key(key)
            self._apply_filter()
            return PageResult()

        if is_up(key):
            selected = self._list_state.selected or 0
            if selected <= 0:
                self._focus_filter = True
                return PageResult()
            self._list_state.select(selected - 1)
            return PageResult()
        if is_down(key):
            selected = self._list_state.selected or 0
            self._list_state.select(min(max(0, len(self._filtered) - 1), selected + 1))
            return PageResult()
        if is_enter(key) and self._filtered:
            branch = self._filtered[self._list_state.selected or 0]
            ok, error = checkout_branch(ctx.repo, branch)
            if ok:
                return PageResult(action=PageAction.POP, repo_changed=True)
            self._error = error
            return PageResult()
        # Typing jumps back to filter.
        self._focus_filter = True
        self._filter_state.focus()
        self._filter_state.handle_key(key)
        self._apply_filter()
        return PageResult()

    def _try_create_branch(self, ctx: PageContext) -> PageResult:
        ok, error = create_branch_from_current(ctx.repo, self._filter_state.value())
        if ok:
            return PageResult(action=PageAction.POP, repo_changed=True)
        self._error = error
        return PageResult()

    def _reload_branches(self) -> None:
        if self._ctx is None:
            self._branches = []
            self._filtered = []
            return
        self._branches = list_local_branches(self._ctx.repo)
        self._apply_filter()

    def _apply_filter(self) -> None:
        needle = self._filter_state.value().casefold()
        if needle:
            self._filtered = [branch for branch in self._branches if needle in branch.casefold()]
        else:
            self._filtered = list(self._branches)
        selected = self._list_state.selected or 0
        self._list_state.select(min(selected, max(0, len(self._filtered) - 1)))


def branch_picker_page() -> BranchPickerPage:
    return BranchPickerPage()