#!/usr/bin/env python3

"""Application.services.plot_display_service
Where: Application service for displaying plot elements.
What: Displays plot elements in checklist format before writing.
Why: Helps authors understand plot requirements before starting to write.
"""

from __future__ import annotations

"""プロット表示サービス

執筆開始前にプロット要素をチェックリスト形式で表示する
SPEC-PLOT-ADHERENCE-001 Section 2.1 準拠実装
"""

from typing import TYPE_CHECKING, Any

from noveler.application.adapters.cli_adapter_factory import CliAdapterFactory
from noveler.application.validators.plot_adherence_validator import AdherenceElementType, PlotElement

if TYPE_CHECKING:
    from noveler.domain.interfaces.i_console_service import IConsoleService
    from noveler.domain.interfaces.i_logger_service import ILoggerService

logger = CliAdapterFactory.create_logger(__name__)


class PlotDisplayService:
    """プロット表示サービス

    執筆開始前にプロット要素を視覚的に表示し、
    執筆者が要求される要素を事前に把握できるようにする。
    """

    def __init__(
        self,
        console_service: IConsoleService | None = None,
        logger_service: ILoggerService | None = None,
    ) -> None:
        """初期化

        Args:
            console_service: コンソール出力サービス（任意）
            logger_service: ロガーサービス（任意）
        """
        if console_service is None:
            console_service = CliAdapterFactory.get_console()  # type: ignore[no-untyped-call]
        if logger_service is None:
            from noveler.domain.interfaces.logger_service import NullLoggerService

            logger_service = NullLoggerService()

        self.console = console_service
        self.logger_service = logger_service
        self._module_logger = logger

        # 要素タイプ別の表示設定
        self._element_display_config = {
            AdherenceElementType.KEY_EVENT: {
                "icon": "🎯",
                "label": "重要イベント",
                "weight_label": "40%",
            },
            AdherenceElementType.CHARACTER_DEVELOPMENT: {
                "icon": "👤",
                "label": "キャラクター描写",
                "weight_label": "30%",
            },
            AdherenceElementType.WORLD_BUILDING: {
                "icon": "🌍",
                "label": "世界観",
                "weight_label": "20%",
            },
            AdherenceElementType.FORESHADOWING: {
                "icon": "💡",
                "label": "伏線",
                "weight_label": "10%",
            },
        }

    def display_plot_elements(
        self,
        episode_number: int,
        plot_data: dict[str, Any],
    ) -> None:
        """プロット要素をチェックリスト形式で表示

        Args:
            episode_number: エピソード番号
            plot_data: プロットデータ（YAML形式）
        """
        self._module_logger.info("プロット要素表示開始: 第%03d話", episode_number)

        try:
            # プロット要素抽出
            plot_elements = self._extract_plot_elements(plot_data)

            if not plot_elements:
                self.console.print_warning("プロット要素が見つかりませんでした")
                return

            # ヘッダー表示
            self._display_header(episode_number, len(plot_elements))

            # 要素タイプ別にグループ化して表示
            self._display_elements_by_type(plot_elements)

            # フッター表示
            self._display_footer()

        except Exception as e:
            self._module_logger.exception("プロット表示エラー: %s", e)
            self.console.print_error(f"プロット表示中にエラーが発生しました: {e}")

    def _extract_plot_elements(self, plot_data: dict[str, Any]) -> list[PlotElement]:
        """プロットデータから要素を抽出

        Args:
            plot_data: プロットYAMLデータ

        Returns:
            list[PlotElement]: プロット要素リスト

        Raises:
            ValueError: 必須キーが欠けている、またはデータ形式が不正な場合
        """
        # 必須キーの検証
        required_keys = ["key_events", "character_development", "world_building", "foreshadowing"]
        missing_keys = [key for key in required_keys if key not in plot_data]

        if missing_keys:
            msg = f"プロットデータに必須キーが欠けています: {', '.join(missing_keys)}"
            raise ValueError(msg)

        elements: list[PlotElement] = []

        # キーイベント抽出
        key_events = plot_data["key_events"]
        if key_events is None:
            msg = "key_eventsがNullです"
            raise ValueError(msg)
        if not isinstance(key_events, list):
            msg = f"key_eventsはリスト型である必要があります（現在: {type(key_events).__name__}）"
            raise ValueError(msg)

        elements.extend(
            PlotElement(
                element_type=AdherenceElementType.KEY_EVENT,
                description=str(event),
                required=True,
            )
            for event in key_events
        )

        # キャラクター要素抽出
        character_development = plot_data["character_development"]
        if character_development is None:
            msg = "character_developmentがNullです"
            raise ValueError(msg)
        if not isinstance(character_development, list):
            msg = f"character_developmentはリスト型である必要があります（現在: {type(character_development).__name__}）"
            raise ValueError(msg)

        elements.extend(
            PlotElement(
                element_type=AdherenceElementType.CHARACTER_DEVELOPMENT,
                description=str(char_element),
                required=True,
            )
            for char_element in character_development
        )

        # 世界観要素抽出
        world_building = plot_data["world_building"]
        if world_building is None:
            msg = "world_buildingがNullです"
            raise ValueError(msg)
        if not isinstance(world_building, list):
            msg = f"world_buildingはリスト型である必要があります（現在: {type(world_building).__name__}）"
            raise ValueError(msg)

        elements.extend(
            PlotElement(
                element_type=AdherenceElementType.WORLD_BUILDING,
                description=str(world_element),
                required=True,
            )
            for world_element in world_building
        )

        # 伏線要素抽出
        foreshadowing = plot_data["foreshadowing"]
        if foreshadowing is None:
            msg = "foreshadowingがNullです"
            raise ValueError(msg)
        if not isinstance(foreshadowing, list):
            msg = f"foreshadowingはリスト型である必要があります（現在: {type(foreshadowing).__name__}）"
            raise ValueError(msg)

        elements.extend(
            PlotElement(
                element_type=AdherenceElementType.FORESHADOWING,
                description=str(foreshadow_element),
                required=True,
            )
            for foreshadow_element in foreshadowing
        )

        self._module_logger.debug("抽出されたプロット要素: %s個", len(elements))
        return elements

    def _display_header(self, episode_number: int, total_elements: int) -> None:
        """ヘッダー表示

        Args:
            episode_number: エピソード番号
            total_elements: 要素総数
        """
        self.console.print("")
        self.console.print("━" * 60)
        self.console.print(f"📋 第{episode_number:03d}話 プロット要素チェックリスト")
        self.console.print(f"   合計 {total_elements} 個の要素を実装してください")
        self.console.print("━" * 60)
        self.console.print("")

    def _display_elements_by_type(self, plot_elements: list[PlotElement]) -> None:
        """要素タイプ別にグループ化して表示

        Args:
            plot_elements: プロット要素リスト
        """
        for element_type in AdherenceElementType:
            # このタイプの要素をフィルタ
            type_elements = [e for e in plot_elements if e.element_type == element_type]

            if not type_elements:
                continue

            # タイプヘッダー
            config = self._element_display_config[element_type]
            self.console.print(
                f"{config['icon']} {config['label']} (重み: {config['weight_label']})"
            )

            # 要素リスト
            for element in type_elements:
                required_marker = "✓" if element.required else "○"
                self.console.print(f"  [{required_marker}] {element.description}")

            self.console.print("")

    def _display_footer(self) -> None:
        """フッター表示"""
        self.console.print("━" * 60)
        self.console.print("💡 ヒント:")
        self.console.print("   • 各要素を原稿に盛り込むことで準拠率が向上します")
        self.console.print("   • 執筆完了後、自動的に準拠率が検証されます")
        self.console.print("   • [✓] は必須要素、[○] は推奨要素です")
        self.console.print("━" * 60)
        self.console.print("")
