# -*- coding: utf-8 -*-
# hub/ticket/domain/ticket_state_service.py

"""Доменный сервис переходов и представления состояния Ticket."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime

from .location_catalog import LocationCatalog
from .task import TicketTask
from .ticket_constants import (
    HARDWARE_SIGNAL_ADVANCE,
    HARDWARE_SIGNAL_INITIALIZE,
    STATE_ARCHIVED,
    STATE_COMPLETED,
    STATE_CONFIRMATION,
    STATE_IN_PROGRESS,
    STATE_REFUSED,
    STATE_TODO,
    TICKET_STATE_ACTIONS,
    TICKET_STATE_COLORS,
    TICKET_STATE_NAMES,
)
from .ticket_transition_policy import TicketTransitionPolicy


STATE_NAMES = TICKET_STATE_NAMES
STATE_COLORS = TICKET_STATE_COLORS


@dataclass(frozen=True, slots=True)
class TicketTransitionResult:
    """Результат обработки доменного перехода Ticket."""

    changed: bool
    task: TicketTask | None
    message: str
    blocked_reason: str = ""


class TicketStateService:
    """Каноническая state-machine Ticket без зависимости от GUI и COM-сервиса."""

    def __init__(
        self,
        location_catalog: LocationCatalog | None = None,
        transition_policy: TicketTransitionPolicy | None = None,
    ):
        self._location_catalog = location_catalog or LocationCatalog()
        self._transition_policy = transition_policy or TicketTransitionPolicy()

    def handle_hardware_signal(
        self,
        button_id: int,
        hardware_state: int,
        current_task: TicketTask | None,
    ) -> TicketTransitionResult:
        """Обработать аппаратный сигнал и вычислить доменный результат."""
        if hardware_state == HARDWARE_SIGNAL_INITIALIZE:
            return TicketTransitionResult(
                changed=False,
                task=current_task,
                message=f"Получен запрос инициализации для кнопки {button_id}.",
            )
        if hardware_state not in HARDWARE_SIGNAL_ADVANCE:
            return TicketTransitionResult(
                changed=False,
                task=current_task,
                message=f"Неизвестное аппаратное состояние: {hardware_state:02X}.",
                blocked_reason="unsupported_hardware_signal",
            )
        return self.advance_task(button_id, current_task)

    def advance_task(self, button_id: int, current_task: TicketTask | None) -> TicketTransitionResult:
        """Перевести задачу в следующее допустимое состояние."""
        if current_task is None:
            task = self._build_task_for_state(button_id, STATE_TODO)
            return TicketTransitionResult(
                changed=True,
                task=task,
                message=f"Создана новая задача #{button_id} в состоянии '{task.state_name}'.",
            )

        if current_task.state_code == STATE_ARCHIVED:
            task = self._reset_task_cycle(current_task, STATE_TODO)
            return TicketTransitionResult(
                changed=True,
                task=task,
                message=f"Архивная задача #{button_id} начала новый цикл.",
            )

        if current_task.state_code == STATE_REFUSED:
            task = self._reset_task_cycle(current_task, STATE_TODO)
            return TicketTransitionResult(
                changed=True,
                task=task,
                message=f"Задача #{button_id} сброшена из отказа в новое выполнение.",
            )

        if current_task.state_code == STATE_COMPLETED:
            task = self._reset_task_cycle(current_task, STATE_TODO)
            return TicketTransitionResult(
                changed=True,
                task=task,
                message=f"Выполненная задача #{button_id} начала новый цикл.",
            )

        decision = self._transition_policy.can_advance(current_task)
        if not decision.allowed:
            return TicketTransitionResult(
                changed=False,
                task=current_task,
                message=f"Переход задачи #{button_id} отклонён.",
                blocked_reason=decision.reason,
            )

        next_state = self._next_state(current_task.state_code)
        task = self._clone_with_state(current_task, next_state)
        return TicketTransitionResult(
            changed=True,
            task=task,
            message=(
                f"Задача #{button_id} перешла из состояния "
                f"'{STATE_NAMES[current_task.state_code]}' в '{task.state_name}'."
            ),
        )

    def mark_task_as_refused(
        self,
        button_id: int,
        current_task: TicketTask | None,
        refusal_reason: str = "",
    ) -> TicketTransitionResult:
        """Перевести задачу в состояние отказа."""
        task = current_task or self._build_task_for_state(button_id, STATE_TODO)
        refused_task = self._clone_with_state(task, STATE_REFUSED)
        refused_task.refused_from_state = task.state_code
        refused_task.refusal_reason = str(refusal_reason or "").strip()
        return TicketTransitionResult(
            changed=True,
            task=refused_task,
            message=f"Задача #{button_id} переведена в отказ.",
        )

    def move_task_to_archive(
        self,
        button_id: int,
        current_task: TicketTask | None,
    ) -> TicketTransitionResult:
        """Переместить задачу в архив как отдельное доменное правило."""
        task = current_task or self._build_task_for_state(button_id, STATE_TODO)
        archived_task = self._clone_with_state(task, STATE_ARCHIVED)
        return TicketTransitionResult(
            changed=True,
            task=archived_task,
            message=f"Задача #{button_id} перемещена в архив.",
        )

    def _next_state(self, current_state: int) -> int:
        if current_state == STATE_TODO:
            return STATE_IN_PROGRESS
        if current_state == STATE_IN_PROGRESS:
            return STATE_CONFIRMATION
        if current_state == STATE_CONFIRMATION:
            return STATE_COMPLETED
        return current_state

    def _build_task_for_state(self, button_id: int, state_code: int) -> TicketTask:
        """Создать новую задачу для button_id в заданном состоянии."""
        return TicketTask(
            task_id=button_id,
            location=self._location_catalog.get_location(button_id),
            state_code=state_code,
            state_name=STATE_NAMES[state_code],
            action_text=TICKET_STATE_ACTIONS[state_code],
            color_hex=STATE_COLORS[state_code],
            created_at=datetime.now(),
            completed_at=datetime.now() if state_code == STATE_COMPLETED else None,
        )

    def _clone_with_state(self, task: TicketTask, state_code: int) -> TicketTask:
        """Склонировать задачу с новым доменным состоянием."""
        return TicketTask(
            task_id=task.task_id,
            location=task.location,
            state_code=state_code,
            state_name=STATE_NAMES[state_code],
            action_text=TICKET_STATE_ACTIONS[state_code],
            color_hex=STATE_COLORS[state_code],
            created_at=task.created_at,
            completed_at=datetime.now() if state_code == STATE_COMPLETED else task.completed_at,
            refused_from_state=task.refused_from_state,
            refusal_reason=task.refusal_reason,
            assigned_specialist=task.assigned_specialist,
            specialist_photo=task.specialist_photo,
            diagnostic_report_signed=task.diagnostic_report_signed,
            repair_report_signed=task.repair_report_signed,
            acceptance_report_signed=task.acceptance_report_signed,
            sequence_number=task.sequence_number,
        )

    def _reset_task_cycle(self, task: TicketTask, state_code: int) -> TicketTask:
        """Начать новый цикл задачи на основе архивной или отказной записи."""
        fresh_task = self._build_task_for_state(task.task_id, state_code)
        fresh_task.location = task.location
        return fresh_task
