# -*- coding: utf-8 -*-
# hub/ticket/ui/ticket_board_page.py

"""Ticket board page."""

from __future__ import annotations

from PySide6.QtWidgets import QSizePolicy

from gui.components import Label
from gui.containers import HContainer, ScrollContainer, SContainer, VContainer

from application import TaskApplicationService
from domain import TicketTaskSnapshot
from domain.ticket_constants import (
    STATE_COMPLETED,
    STATE_CONFIRMATION,
    STATE_IN_PROGRESS,
    STATE_REFUSED,
    STATE_TODO,
)
from .cards import TaskCard
from .details import TaskDetailsDialog


class _TicketBoardColumn(SContainer):
    """Single Ticket board column."""

    def __init__(self, parent=None):
        super().__init__(spacing=12, parent=parent)
        self._cards: dict[object, TaskCard] = {}
        self._badge_label: Label | None = None
        self._card_host: VContainer | None = None
        self._setup_ui()

    def _setup_ui(self) -> None:
        # Верхний header-контейнер колонки: объединяет счётчик задач и заголовок этапа.
        header = HContainer(
            height_percent=5.37,
            margin=0,
            spacing=16,
            content_fit=False,
            style="TICKET_BOARD_COLUMN_HEADER",
            parent=self,
        )

        # Badge-shell слева в header: резервирует место под цветной счётчик статуса.
        header.add_widget(self._build_badge(header))
        # Центральный текстовый блок header: показывает название этапа колонки.
        header.add_widget(self._build_title_label())
        header.add_stretch()

        # Body-shell колонки: визуальная подложка под список карточек текущего этапа.
        body = SContainer(
            margin=0,
            style="TICKET_BOARD_COLUMN_BODY",
            parent=self,
        )

        # Scroll-host внутри body: даёт колонке собственную область прокрутки карточек.
        scroll = ScrollContainer(
            margin=0,
            spacing=0,
            orientation="v",
            vertical_scroll_bar_policy="always_off",
            horizontal_scroll_bar_policy="always_off",
            style="SCROLL_CONTAINER",
            parent=body,
        )
        scroll.scroll_area.verticalScrollBar().setSingleStep(48)

        # Card-host: вертикальный стек карточек, который динамически растёт по содержимому.
        self._card_host = VContainer(
            spacing=12,
            content_fit=False,
            parent=scroll,
        )
        self._card_host.set_size_policy(
            QSizePolicy.Policy.Expanding,
            QSizePolicy.Policy.Fixed,
        )
        # Подписка на фазу 2 каскада percent-sized для детей _card_host.
        # Срабатывает после того, как все карточки получили parent_resized,
        # отработали _on_parent_rebuild_finished -> _sync_card_height ->
        # setFixedHeight(target). К этому моменту minimumHeight каждой
        # карточки уже отражает её итоговую высоту, и сумма stack_height
        # вычисляется корректно за один проход без промежуточных «дёрганий».
        self._card_host.on_children_rebuild_finished(self._sync_card_host_height)
        self._sync_card_host_height()

    def _build_badge(self, parent) -> SContainer:
        raise NotImplementedError

    def _build_title_label(self) -> Label:
        raise NotImplementedError

    def add_card(self, card: TaskCard) -> None:
        if self._card_host is None:
            return
        self.remove_card(card.card_id)
        # Подписка на per-card сигнал нужна для сценария add без resize
        # колонки: _card_host не получает Resize event, его phase-2 emitter
        # молчит, поэтому пересчёт стека инициирует сама карточка после
        # своего первого _sync_card_height -> setFixedHeight().
        card.card_height_changed.connect(self._sync_card_host_height)
        self._cards[card.card_id] = card
        self._card_host.insert_widget(len(self._cards) - 1, card)
        # Синхронный _sync_card_host_height НЕ вызываем: высота карточки
        # ещё нулевая (фаза 2 каскада придёт через QTimer.singleShot(0)).
        # Пересчёт стека выполнится по card_height_changed (add без resize)
        # либо по _card_host.parent_rebuild_finished (add с resize).
        self._update_counter()

    def remove_card(self, card_id: object) -> TaskCard | None:
        card = self._cards.pop(card_id, None)
        if card is None or self._card_host is None:
            return None
        self._card_host.remove_widget(card)
        card.setParent(None)
        # Удалённая карточка фазу 2 уже не пришлёт; синхронный пересчёт
        # обязателен, иначе stack_height застрянет на устаревшей сумме.
        self._sync_card_host_height()
        self._update_counter()
        return card

    def clear_cards(self) -> None:
        for card in list(self._cards.values()):
            if self._card_host is not None:
                self._card_host.remove_widget(card)
            card.setParent(None)
        self._cards.clear()
        # Аналогично remove_card: после очистки фазы 2 от детей не будет.
        self._sync_card_host_height()
        self._update_counter()

    def _update_counter(self) -> None:
        if self._badge_label is not None:
            self._badge_label.set_text(str(len(self._cards)))

    def _sync_card_host_height(self) -> None:
        if self._card_host is None:
            return
        # Используем card.minimumHeight(): после _sync_card_height() карточка
        # вызывает setFixedHeight(target), который атомарно выставляет
        # min == max == target. В отличие от card.height(), это значение
        # доступно сразу, до прогона layout-цикла, что гарантирует
        # корректный stack_height в момент эмиссии card_height_changed.
        stack_height = sum(
            max(card.minimumHeight(), card.height())
            for card in self._cards.values()
        )
        card_count = len(self._cards)
        if card_count > 1:
            stack_height += 12 * (card_count - 1)
        self._card_host.set_min_height(stack_height)
        self._card_host.set_max_height(stack_height)


class _TodoTicketBoardColumn(_TicketBoardColumn):
    """Ticket board column for TODO state."""

    def _build_badge(self, parent) -> SContainer:
        # Badge-контейнер колонки TODO: цветной фон и центрирование значения счётчика.
        badge_container = SContainer(
            width_percent=12,
            height_percent=100,
            content_fit=False,
            style="TICKET_BOARD_COUNTER_SHELL_TODO",
            parent=parent,
        )
        self._badge_label = Label(
            "0",
            style="TICKET_BOARD_COUNTER_TEXT_WHITE",
            parent=badge_container,
        )
        return badge_container

    def _build_title_label(self) -> Label:
        return Label("Новая заявка", style="TICKET_BOARD_COLUMN_TITLE")


class _InProgressTicketBoardColumn(_TicketBoardColumn):
    """Ticket board column for IN_PROGRESS state."""

    def _build_badge(self, parent) -> SContainer:
        # Badge-контейнер колонки IN_PROGRESS: цветовой маркер и число задач в работе.
        badge_container = SContainer(
            width_percent=12,
            height_percent=100,
            content_fit=False,
            style="TICKET_BOARD_COUNTER_SHELL_IN_PROGRESS",
            parent=parent,
        )
        self._badge_label = Label(
            "0",
            style="TICKET_BOARD_COUNTER_TEXT_WHITE",
            parent=badge_container,
        )
        return badge_container

    def _build_title_label(self) -> Label:
        return Label("Заявка принята к работе", style="TICKET_BOARD_COLUMN_TITLE")


class _ConfirmationTicketBoardColumn(_TicketBoardColumn):
    """Ticket board column for CONFIRMATION state."""

    def _build_badge(self, parent) -> SContainer:
        # Badge-контейнер колонки CONFIRMATION: показывает объём задач на подтверждении.
        badge_container = SContainer(
            width_percent=12,
            height_percent=100,
            content_fit=False,
            style="TICKET_BOARD_COUNTER_SHELL_CONFIRMATION",
            parent=parent,
        )
        self._badge_label = Label(
            "0",
            style="TICKET_BOARD_COUNTER_TEXT_WHITE",
            parent=badge_container,
        )
        return badge_container

    def _build_title_label(self) -> Label:
        return Label("Заявка на подтверждении", style="TICKET_BOARD_COLUMN_TITLE")


class _CompletedTicketBoardColumn(_TicketBoardColumn):
    """Ticket board column for COMPLETED state."""

    def _build_badge(self, parent) -> SContainer:
        # Badge-контейнер колонки COMPLETED: визуально отделяет завершённые задачи.
        badge_container = SContainer(
            width_percent=12,
            height_percent=100,
            content_fit=False,
            style="TICKET_BOARD_COUNTER_SHELL_COMPLETED",
            parent=parent,
        )
        self._badge_label = Label(
            "0",
            style="TICKET_BOARD_COUNTER_TEXT_WHITE",
            parent=badge_container,
        )
        return badge_container

    def _build_title_label(self) -> Label:
        return Label("Заявка закрыта", style="TICKET_BOARD_COLUMN_TITLE")


class _RefusedTicketBoardColumn(_TicketBoardColumn):
    """Ticket board column for REFUSED state."""

    def _build_badge(self, parent) -> SContainer:
        # Badge-контейнер колонки REFUSED: выделяет счётчик задач со статусом отказа.
        badge_container = SContainer(
            width_percent=12,
            height_percent=100,
            content_fit=False,
            style="TICKET_BOARD_COUNTER_SHELL_REFUSED",
            parent=parent,
        )
        self._badge_label = Label(
            "0",
            style="TICKET_BOARD_COUNTER_TEXT_MUTED",
            parent=badge_container,
        )
        return badge_container

    def _build_title_label(self) -> Label:
        return Label("Отменённая заявка", style="TICKET_BOARD_COLUMN_TITLE")


class TicketBoardPage(SContainer):
    """Ticket board connected to application signals."""

    def __init__(self, application: TaskApplicationService, parent=None):
        super().__init__(
            width_percent=100,
            height_percent=100,
            parent=parent,
            style="TICKET_SHELL_ROOT",
        )
        self._application = application
        self._columns: dict[int, _TicketBoardColumn] = {}
        self._task_columns: dict[int, int] = {}
        self._setup_ui()
        self._connect_signals()
        self._reload_board()

    def _setup_ui(self) -> None:
        # Главный row-контейнер доски: раскладывает все статусные колонки по горизонтали.
        board_row = HContainer(
            margin=[0, 0, 0, 0],
            height_percent=100,
            spacing=16,
            style="TICKET_SURFACE_HOST",
            parent=self,
        )

        todo_column = _TodoTicketBoardColumn()
        in_progress_column = _InProgressTicketBoardColumn()
        confirmation_column = _ConfirmationTicketBoardColumn()
        completed_column = _CompletedTicketBoardColumn()
        refused_column = _RefusedTicketBoardColumn()

        self._columns[STATE_TODO] = todo_column
        self._columns[STATE_IN_PROGRESS] = in_progress_column
        self._columns[STATE_CONFIRMATION] = confirmation_column
        self._columns[STATE_COMPLETED] = completed_column
        self._columns[STATE_REFUSED] = refused_column

        board_row.add_widget(todo_column)
        board_row.add_widget(in_progress_column)
        board_row.add_widget(confirmation_column)
        board_row.add_widget(completed_column)
        board_row.add_widget(refused_column)

    def _connect_signals(self) -> None:
        self._application.task_updated.connect(self._on_task_updated)
        self._application.task_removed.connect(self._on_task_removed)
        self._application.state_loaded.connect(self._reload_board)

    def _reload_board(self, *_args) -> None:
        for column in self._columns.values():
            column.clear_cards()
        self._task_columns.clear()
        for task in self._application.list_active_tasks():
            self._upsert_task(task)

    def _upsert_task(self, task: TicketTaskSnapshot) -> None:
        column = self._columns.get(task.state_code)
        if column is None:
            self._remove_task(task.task_id)
            return
        self._remove_task(task.task_id)
        card = TaskCard(task)
        card.card_clicked.connect(self._on_card_clicked)
        column.add_card(card)
        self._task_columns[task.task_id] = task.state_code

    def _remove_task(self, task_id: int) -> None:
        state_code = self._task_columns.pop(task_id, None)
        if state_code is not None:
            column = self._columns.get(state_code)
            if column is not None:
                column.remove_card(task_id)
            return
        for column in self._columns.values():
            if column.remove_card(task_id) is not None:
                return

    def _on_task_updated(self, task: TicketTaskSnapshot) -> None:
        if isinstance(task, TicketTaskSnapshot):
            self._upsert_task(task)

    def _on_task_removed(self, task_id: int) -> None:
        self._remove_task(task_id)

    def _on_card_clicked(self, task_id: object) -> None:
        try:
            normalized_task_id = int(task_id)
        except (TypeError, ValueError):
            return
        task = self._application.get_task(normalized_task_id)
        if task is None:
            return
        TaskDetailsDialog(
            application=self._application,
            task_id=normalized_task_id,
            parent=self,
        ).exec()
