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

"""Оболочка модуля Ticket в составе независимого приложения Dispatch.

Назначение модуля:
    Минимальная shell-страница Dispatch:
    - Единая горизонтальная шапка из трёх равных по ширине кнопок:
      кнопка-логотип (открывает контактный диалог), кнопка раздела
      «Ваши заявки», кнопка авторизации `Log In` / `Log Out`.
    - Центральная область с доской задач Ticket.
    - Модуль работы с COM-портом в Dispatch отключён, поэтому строка
      состояния COM-канала и подписки на сигналы шлюза удалены.

Архитектурные ограничения:
    - Стилевые ключи берутся только из внешнего реестра `APP_STYLES`;
      локальные QSS-литералы не используются.
    - Все три элемента шапки — экземпляры локальной обёртки `Button`
      и распределяют пространство равными долями `stretch=1`.
    - Логотип реализован как канонический `Button` с иконкой
      (`icon_path`), без кастомного raw-Qt компонента и без inline QSS.
"""

from __future__ import annotations

import os
from functools import partial

from gui.components.button import Button
from gui.components.dialog import Dialog
from gui.components.label import Label
from gui.containers import HContainer, SContainer, StackContainer, VContainer
from gui.theme_bus import theme_bus

from application import TaskApplicationService
from .pages import ArchivePage
from .ticket_board_page import TicketBoardPage
from .ticket_create_page import TicketCreatePage


# Контактные телефоны для модального окна. Текст вынесен в константы,
# чтобы единственное место правки находилось в верхней части файла.
_CONTACT_PHONE_GENERAL = "+7 (000) 000-00-00"
_CONTACT_PHONE_DISPATCHER = "+7 (000) 000-00-00"
_CONTACT_PHONE_SERVICE_HEAD = "+7 (000) 000-00-00"


class _ContactsDialog(Dialog):
    """Простое модальное окно с контактными данными организации."""

    def __init__(self, parent=None):
        super().__init__(
            title="Контактные данные",
            width=420,
            height=260,
            modal=True,
            parent=parent,
        )
        self._setup_ui()

    def _setup_ui(self) -> None:
        body = VContainer(
            margin=[24, 20, 24, 20],
            spacing=12,
            parent=None,
        )
        self.add_widget(body)

        body.add_widget(Label(
            "Контакты службы сервисного обслуживания",
            height_percent=20,
            style="LOGIN_TITLE",
        ))
        body.add_widget(Label(
            f"Общий телефон: {_CONTACT_PHONE_GENERAL}",
            height_percent=20,
            style="LOGIN_FIELD_LABEL",
        ))
        body.add_widget(Label(
            f"Диспетчер: {_CONTACT_PHONE_DISPATCHER}",
            height_percent=20,
            style="LOGIN_FIELD_LABEL",
        ))
        body.add_widget(Label(
            f"Руководитель службы: {_CONTACT_PHONE_SERVICE_HEAD}",
            height_percent=20,
            style="LOGIN_FIELD_LABEL",
        ))

        actions = SContainer(
            height_percent=20,
            orientation="h",
            content_fit=False,
        )
        actions.add_stretch(1)
        close_button = Button(
            text="Закрыть",
            height_percent=100,
            margin=0,
            content_fit=False,
        )
        close_button.clicked.connect(self.accept)
        actions.add_widget(close_button)
        body.add_widget(actions)


class TicketShell(SContainer):
    """Корневая оболочка Dispatch: шапка из трёх кнопок и доска задач."""

    def __init__(
        self,
        application: TaskApplicationService,
        parent=None,
    ):
        super().__init__(
            width_percent=100,
            height_percent=100,
            margin=0,
            spacing=0,
            parent=parent,
            style="TICKET_SHELL_ROOT",
        )
        self._application = application
        self._page_stack: StackContainer | None = None
        self._theme = "dark" if self.palette().window().color().lightness() < 128 else "light"
        self._active_view_name = "board"
        self._page_index_by_name: dict[str, int] = {}
        self._nav_buttons: dict[str, Button] = {}
        self._login_button: Button | None = None
        self._logo_button: Button | None = None
        self._is_logged_in: bool = False
        self._logged_in_user: dict | None = None
        self._create_page: TicketCreatePage | None = None
        self._logo_dark_path, self._logo_light_path = self._resolve_logo_paths()
        self._setup_ui()
        self._connect_signals()
        self._sync_from_application()
        self._restore_session()

    # ── разметка интерфейса ──

    def _setup_ui(self) -> None:
        # Единая шапка: один HContainer, три равные по ширине кнопки.
        # Высота шапки — 6% высоты shell. Распределение долей
        # выполняется через `add_widget_with_stretch(button, 1)` для
        # каждой кнопки, что даёт ровно одинаковые сегменты.
        top_bar = HContainer(
            height_percent=6,
            margin=[0, 0, 0, 12],
            spacing=16,
            content_fit=False,
            style="TICKET_SURFACE_HOST",
            parent=self,
        )

        # Кнопка-логотип: при нажатии открывается модальный диалог с
        # контактными данными. Иконка логотипа подбирается под тему.
        self._logo_button = Button(
            text="",
            height_percent=100,
            margin=0,
            is_active=False,
            content_fit=False,
            icon_path=self._logo_dark_path,
            icon_size=40,
        )

        board_button = Button(
            text="Ваши заявки",
            height_percent=100,
            margin=0,
            is_active=True,
            content_fit=False,
        )
        self._nav_buttons["board"] = board_button

        create_button = Button(
            text="Создать заявку",
            height_percent=100,
            margin=0,
            is_active=False,
            content_fit=False,
        )
        self._nav_buttons["create"] = create_button

        archive_button = Button(
            text="Архив моих заявок",
            height_percent=100,
            margin=0,
            is_active=False,
            content_fit=False,
        )
        self._nav_buttons["archive"] = archive_button

        self._login_button = Button(
            text="Log In",
            height_percent=100,
            margin=0,
            is_active=False,
            content_fit=False,
            style="LOGIN_NAV_BUTTON",
        )

        top_bar.add_widget_with_stretch(self._logo_button, 1)
        top_bar.add_widget_with_stretch(board_button, 1)
        top_bar.add_widget_with_stretch(create_button, 1)
        top_bar.add_widget_with_stretch(archive_button, 1)
        top_bar.add_widget_with_stretch(self._login_button, 1)

        # Центральный stack-контейнер: в Dispatch удерживает три страницы — доску, создание и архив.
        self._page_stack = StackContainer(margin=0, parent=self)

        board_page = TicketBoardPage(application=self._application)
        create_page = TicketCreatePage(
            application=self._application,
            on_finish=self._on_create_form_finished,
        )
        self._create_page = create_page
        archive_page = ArchivePage(application=self._application)

        self._page_index_by_name["board"] = self._page_stack.add_widget(board_page)
        self._page_index_by_name["create"] = self._page_stack.add_widget(create_page)
        self._page_index_by_name["archive"] = self._page_stack.add_widget(archive_page)
        self._page_stack.set_current_index(self._page_index_by_name["board"])

    def _connect_signals(self) -> None:
        self._nav_buttons["board"].clicked.connect(partial(self._on_navigation_requested, "board"))
        self._nav_buttons["create"].clicked.connect(partial(self._on_navigation_requested, "create"))
        self._nav_buttons["archive"].clicked.connect(partial(self._on_navigation_requested, "archive"))
        if self._login_button is not None:
            self._login_button.clicked.connect(self._on_login_button_clicked)
        if self._logo_button is not None:
            self._logo_button.clicked.connect(self._on_logo_button_clicked)

        self._application.active_view_changed.connect(self._on_active_view_changed)
        theme_bus.theme_changed.connect(self._on_theme_changed)

    def _sync_from_application(self) -> None:
        self._set_active_page(self._application.get_active_view())

    # ── навигация и страницы ──

    def _set_active_page(self, view_name: str) -> None:
        if self._page_stack is None:
            return
        normalized_name = view_name if view_name in self._page_index_by_name else "board"
        self._active_view_name = normalized_name
        target_index = self._page_index_by_name.get(normalized_name)
        if target_index is None:
            return
        if self._page_stack.current_index() != target_index:
            self._page_stack.set_current_index(target_index)
        self._apply_navigation_theme()

    def _on_active_view_changed(self, view_name: str) -> None:
        self._set_active_page(view_name)

    def _on_navigation_requested(self, view_name: str, _checked: bool = False) -> None:
        if view_name == "create" and self._create_page is not None:
            # При каждом входе на страницу форма подтягивает актуальные данные заявителя.
            self._create_page.refresh_user_session()
        self._application.set_active_view(view_name)

    def _on_create_form_finished(self) -> None:
        """После сохранения или отмены формы вернуть пользователя на доску заявок."""
        self._application.set_active_view("board")

    # ── авторизация ──

    def _on_login_button_clicked(self, _checked: bool = False) -> None:
        """Переключить состояние сессии: вход через диалог или выход.

        Источник учётных данных — каталог `DB_dispatch`. Сценарий
        полностью повторяет схему USMS: при отсутствии активного
        пользователя открывается модальный диалог авторизации,
        иначе выполняется выход и очистка файла активной сессии.
        """
        if self._login_button is None:
            return
        if self._logged_in_user is not None:
            self._do_logout()
        else:
            self._do_login()

    def _do_login(self) -> None:
        """Открыть диалог авторизации и при успехе записать сессию."""
        # Импорты вынесены в момент вызова, чтобы избежать циклической
        # загрузки модулей при старте приложения Dispatch.
        from gui.login_dialog import LoginDialog
        from auth_service import write_session

        dialog = LoginDialog(parent=self)
        if dialog.exec() != LoginDialog.DialogCode.Accepted:
            return
        user = dialog.get_authenticated_user()
        if user is None:
            return
        write_session(user)
        self._apply_logged_in_state(user)

    def _do_logout(self) -> None:
        """Очистить запись активной сессии и вернуть кнопку в Log In."""
        from auth_service import clear_session

        clear_session()
        self._apply_logged_out_state()

    def _restore_session(self) -> None:
        """Восстановить состояние входа из `DB_dispatch/1_actual_state.py`."""
        from auth_service import load_session

        user = load_session()
        if user is not None:
            self._apply_logged_in_state(user)

    def _apply_logged_in_state(self, user: dict) -> None:
        """Перевести кнопку входа в состояние Log Out для активного пользователя."""
        self._logged_in_user = user
        self._is_logged_in = True
        if self._login_button is None:
            return
        self._login_button.set_text("Log Out")
        self._login_button.style(is_active=True)

    def _apply_logged_out_state(self) -> None:
        """Перевести кнопку входа обратно в состояние Log In."""
        self._logged_in_user = None
        self._is_logged_in = False
        if self._login_button is None:
            return
        self._login_button.set_text("Log In")
        self._login_button.style(is_active=False)

    # ── контактный диалог ──

    def _on_logo_button_clicked(self, _checked: bool = False) -> None:
        """Открыть модальное окно с контактными телефонами организации."""
        dialog = _ContactsDialog(parent=self)
        dialog.exec()

    # ── оформление и тема ──

    def _on_theme_changed(self, theme: str) -> None:
        self._apply_theme(theme)

    def _apply_theme(self, theme: str) -> None:
        normalized_theme = (theme or "").strip().lower()
        if normalized_theme not in {"dark", "light"}:
            return
        self._theme = normalized_theme
        self._apply_navigation_theme()
        self._update_logo_icon(normalized_theme)

    def _apply_navigation_theme(self) -> None:
        normal_key, active_key = self._navigation_style_keys()
        for page_name, button in self._nav_buttons.items():
            button.style(
                style_key=normal_key,
                active_key=active_key,
                is_active=page_name == self._active_view_name,
            )

    def _navigation_style_keys(self) -> tuple[str, str]:
        if self._theme == "light":
            return ("TAB_BUTTON_NORMAL_LIGHT", "TAB_BUTTON_ACTIVE_LIGHT")
        return ("TAB_BUTTON_NORMAL", "TAB_BUTTON_ACTIVE")

    def _resolve_logo_paths(self) -> tuple[str, str]:
        """Вычислить пути файлов логотипа из локального каталога `gui/components/logo`."""
        # __file__ = .../dispatch/hub/ticket/ui/ticket_shell.py
        # project_root = .../dispatch
        project_root = os.path.dirname(
            os.path.dirname(
                os.path.dirname(
                    os.path.dirname(os.path.abspath(__file__))
                )
            )
        )
        logo_dir = os.path.join(project_root, "gui", "components", "logo")
        return (
            os.path.join(logo_dir, "Nutshell_Logo_ENG_White.png"),
            os.path.join(logo_dir, "Nutshell_Logo_ENG_Black.png"),
        )

    def _update_logo_icon(self, theme: str) -> None:
        """Подобрать иконку кнопки-логотипа под текущую тему оформления."""
        if self._logo_button is None:
            return
        is_light = str(theme or "").strip().lower() == "light"
        path = self._logo_light_path if is_light else self._logo_dark_path
        from PySide6.QtCore import QSize
        from PySide6.QtGui import QIcon

        # Используем штатное API QPushButton, доступ к которому Button предоставляет
        # как делегат через свойство `clicked`. Иконку обновляем в обход публичного API,
        # потому что Button не имеет канонического `set_icon` метода в текущем wrapper-слое.
        inner_button = getattr(self._logo_button, "_button", None)
        if inner_button is not None:
            inner_button.setIcon(QIcon(path))
            inner_button.setIconSize(QSize(40, 40))
