"""Task editor page for the pgt TUI."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Literal

from pygit2 import Oid

from pygittools.tasks import BOARD_REF_PREFIX, EMPTY_TREE_OID_HEX, TASK_REF_PREFIX, Task, get_board, get_task
from pygittools.tasks_query import BOARD_STATUS_ORDER, get_task_view
from pygittools.tui.draw import draw_line
from pygittools.tui.git_colors import current_theme, focus_attr
from pygittools.tui.types import PageAction, PageContext, PageResult
from pygittools.tui.ucurses import (  # type: ignore[import-untyped]
	KEY_BACKSPACE,
	KEY_DOWN,
	KEY_ENTER,
	KEY_RESIZE,
	KEY_UP,
)

_FieldName = Literal["title", "description", "status", "priority", "assignee", "due_date"]
_FIELDS: tuple[_FieldName, ...] = ("title", "description", "status", "priority", "assignee", "due_date")
_PRIORITY_ORDER: tuple[str, ...] = ("LOW", "MEDIUM", "HIGH", "CRITICAL")


@dataclass(frozen=True, slots=True)
class _EditorState:
	title: str
	description: str
	status: str | None
	priority: str | None
	assignee: str
	due_date: str


class TaskEditorPage:
	title = "task"

	def __init__(self, *, board_name: str, task_ref: str | None, default_status: str | None) -> None:
		self._ctx: PageContext | None = None
		self._board_name = board_name
		self._task_ref = task_ref
		self._default_status = default_status
		self._state = _EditorState(
			title="",
			description="",
			status=default_status,
			priority=None,
			assignee="",
			due_date="",
		)
		self._field_index = 0
		self._input_mode: Literal["nav", "input"] = "input"
		self._error: str | None = None

	def on_enter(self, ctx: PageContext) -> None:
		self._ctx = ctx
		if self._task_ref is None:
			return
		view = get_task_view(ctx.repo, self._task_ref)
		self._state = _EditorState(
			title=view.get("title", ""),
			description=view.get("description", ""),
			status=view.get("status"),
			priority=view.get("priority"),
			assignee=view.get("assignee") or "",
			due_date=view.get("due_date") or "",
		)

	def status_text(self) -> str:
		if self._error:
			return self._error
		return "Enter save — j/k move field — type to edit — Esc or q back"

	def draw(self, stdscr: int, height: int, width: int) -> None:
		del stdscr
		if height <= 0:
			return
		theme = current_theme()
		header = f"Task in board {self._board_name}"
		draw_line(0, 0, header[:width], width, theme.header)
		if height <= 1:
			return

		rows = [
			f"Title: {self._state.title}",
			f"Description: {self._state.description}",
			f"Status: {self._state.status or '(none)'}",
			f"Priority: {self._state.priority or '(none)'}",
			f"Assignee: {self._state.assignee}",
			f"Due date (ISO): {self._state.due_date}",
		]
		for index, text in enumerate(rows):
			if index + 1 >= height:
				break
			attr = theme.normal
			if index == self._field_index:
				attr = focus_attr(attr)
			draw_line(index + 1, 0, text[:width], width, attr)

	def handle_key(self, key: int) -> PageResult:
		if key in (ord("q"), ord("Q")):
			return PageResult(action=PageAction.POP)
		if key == 27:
			if self._input_mode == "input":
				self._input_mode = "nav"
				return PageResult()
			return PageResult(action=PageAction.POP)
		if key == KEY_RESIZE:
			return PageResult()
		if self._error is not None:
			self._error = None

		field = _FIELDS[self._field_index]

		# Input mode: edit text fields, cycle status/priority; j/k/h/l are text in text fields.
		if self._input_mode == "input":
			if field in ("title", "description", "assignee", "due_date"):
				if key in (KEY_UP, KEY_DOWN):
					# Move between fields with arrows even while in input mode.
					if key == KEY_UP:
						self._field_index = max(0, self._field_index - 1)
					else:
						self._field_index = min(len(_FIELDS) - 1, self._field_index + 1)
					return PageResult()
				if key in (KEY_ENTER, 10, 13):
					return self._save()
				return self._edit_text_field(field, key)
			if field == "status":
				self._cycle_status(key)
				return PageResult()
			if field == "priority":
				self._cycle_priority(key)
				return PageResult()
			return PageResult()

		# Nav mode: j/k and arrows move between fields; Enter saves.
		if key in (KEY_UP, ord("k"), ord("K")):
			self._field_index = max(0, self._field_index - 1)
			return PageResult()
		if key in (KEY_DOWN, ord("j"), ord("J")):
			self._field_index = min(len(_FIELDS) - 1, self._field_index + 1)
			return PageResult()
		if key in (KEY_ENTER, 10, 13):
			return self._save()
		return PageResult()

	def _edit_text_field(self, field: _FieldName, key: int) -> PageResult:
		if key in (KEY_BACKSPACE, 127, 8):
			self._state = _replace_state_field(self._state, field, _text_for_field(self._state, field)[:-1])
			return PageResult()
		if 32 <= key <= 126:
			text = _text_for_field(self._state, field) + chr(key)
			self._state = _replace_state_field(self._state, field, text)
			return PageResult()
		return PageResult()

	def _cycle_status(self, key: int) -> None:
		if key not in (KEY_UP, KEY_DOWN, ord("j"), ord("J"), ord("k"), ord("K")):
			return
		current = self._state.status or self._default_status or BOARD_STATUS_ORDER[0]
		try:
			index = BOARD_STATUS_ORDER.index(current)
		except ValueError:
			index = 0
		if key in (KEY_UP, ord("k"), ord("K")):
			index = (index - 1) % len(BOARD_STATUS_ORDER)
		else:
			index = (index + 1) % len(BOARD_STATUS_ORDER)
		self._state = _EditorState(
			title=self._state.title,
			description=self._state.description,
			status=BOARD_STATUS_ORDER[index],
			priority=self._state.priority,
			assignee=self._state.assignee,
			due_date=self._state.due_date,
		)

	def _cycle_priority(self, key: int) -> None:
		if key not in (KEY_UP, KEY_DOWN, ord("j"), ord("J"), ord("k"), ord("K")):
			return
		if not _PRIORITY_ORDER:
			return
		current = self._state.priority or _PRIORITY_ORDER[0]
		try:
			index = _PRIORITY_ORDER.index(current)
		except ValueError:
			index = 0
		if key in (KEY_UP, ord("k"), ord("K")):
			index = (index - 1) % len(_PRIORITY_ORDER)
		else:
			index = (index + 1) % len(_PRIORITY_ORDER)
		self._state = _EditorState(
			title=self._state.title,
			description=self._state.description,
			status=self._state.status,
			priority=_PRIORITY_ORDER[index],
			assignee=self._state.assignee,
			due_date=self._state.due_date,
		)

	def _save(self) -> PageResult:
		ctx = self._ctx
		if ctx is None:
			return PageResult(action=PageAction.POP)

		try:
			due_dt = _parse_due_date(self._state.due_date)
		except ValueError as exc:
			self._error = str(exc)
			return PageResult()

		if self._task_ref is None:
			try:
				_create_task(ctx, self._board_name, self._state, due_dt)
			except Exception as exc:  # noqa: BLE001
				self._error = str(exc)
				return PageResult()
			return PageResult(action=PageAction.POP)

		task = get_task(ctx.repo, self._task_ref)
		if task is None:
			self._error = f"Task not found: {self._task_ref}"
			return PageResult()
		task.title = self._state.title
		task.description = self._state.description
		task.status = Task.Status(self._state.status) if self._state.status else None
		task.priority = Task.Priority(self._state.priority) if self._state.priority else None
		task.assignee = self._state.assignee or None
		task.due_date = due_dt
		task.update_message()
		task.write(ctx.repo)
		return PageResult(action=PageAction.POP)


def _replace_state_field(state: _EditorState, field: _FieldName, value: str) -> _EditorState:
	if field == "title":
		return _EditorState(
			title=value,
			description=state.description,
			status=state.status,
			priority=state.priority,
			assignee=state.assignee,
			due_date=state.due_date,
		)
	if field == "description":
		return _EditorState(
			title=state.title,
			description=value,
			status=state.status,
			priority=state.priority,
			assignee=state.assignee,
			due_date=state.due_date,
		)
	if field == "assignee":
		return _EditorState(
			title=state.title,
			description=state.description,
			status=state.status,
			priority=state.priority,
			assignee=value,
			due_date=state.due_date,
		)
	return _EditorState(
		title=state.title,
		description=state.description,
		status=state.status,
		priority=state.priority,
		assignee=state.assignee,
		due_date=value,
	)


def _text_for_field(state: _EditorState, field: _FieldName) -> str:
	if field == "title":
		return state.title
	if field == "description":
		return state.description
	if field == "assignee":
		return state.assignee
	return state.due_date


def _parse_due_date(text: str) -> datetime | None:
	if not text.strip():
		return None
	try:
		return datetime.fromisoformat(text)
	except ValueError as exc:
		raise ValueError(f"Invalid due date (expected ISO format): {text}") from exc


def _create_task(ctx: PageContext, board_name: str, state: _EditorState, due_dt: datetime | None) -> None:
	board_ref = f"{BOARD_REF_PREFIX}{board_name}"
	board = get_board(ctx.repo, board_ref)
	if board is None:
		raise ValueError(f"Board not found: {board_name}")
	title = state.title or "Untitled"
	slug = title.lower().replace(" ", "_") or "task"
	full_ref = f"{TASK_REF_PREFIX}{slug}"
	target = Oid(hex=EMPTY_TREE_OID_HEX)
	sig = ctx.repo.default_signature
	tagger = f"{sig.name} <{sig.email}>"
	task = Task(
		target,
		full_ref,
		tagger,
		title=title,
		description=state.description,
		status=Task.Status(state.status) if state.status else None,
		priority=Task.Priority(state.priority) if state.priority else None,
		assignee=state.assignee or None,
		due_date=due_dt,
	)
	task_oid = task.write(ctx.repo)
	board.tasks = [*(getattr(board, "tasks", []) or []), str(task_oid)]
	board.update_message()
	board.write(ctx.repo)