"""プロット関連コマンドハンドラー

SPEC-901-DDD-REFACTORING対応:
- Message Bus統合による既存ユースケースのコマンドハンドラー化
- PlotGenerationUseCaseの機能をコマンドハンドラーとして再実装
"""

from pathlib import Path
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from noveler.domain.interfaces import IConfigurationService, ILoggerService, IPathService
    from noveler.infrastructure.unit_of_work import IUnitOfWork

from noveler.domain.commands.plot_commands import (
    GeneratePlotCommand,
    SavePlotCommand,
    UpdateMasterPlotCommand,
    ValidatePlotCommand,
)
from noveler.domain.entities.generated_episode_plot import GeneratedEpisodePlot
from noveler.domain.events.plot_events import (
    MasterPlotUpdated,
    PlotGenerationCompleted,
    PlotGenerationFailed,
    PlotGenerationStarted,
    PlotQualityCheckCompleted,
    PlotSaved,
)
from noveler.domain.value_objects.project_time import project_now


class PlotGenerationCommandHandler:
    """プロット生成コマンドハンドラー

    既存のPlotGenerationUseCaseをMessage Bus統合向けに変換
    """

    def __init__(
        self,
        logger_service: "ILoggerService",
        unit_of_work: "IUnitOfWork",
        path_service: "IPathService",
        config_service: "IConfigurationService"
    ) -> None:
        """ハンドラー初期化

        Args:
            logger_service: ロガーサービス
            unit_of_work: Unit of Work
            path_service: パスサービス
            config_service: 設定サービス
        """
        self.logger_service = logger_service
        self.uow = unit_of_work
        self.path_service = path_service
        self.config_service = config_service

    def handle(self, command: GeneratePlotCommand) -> GeneratedEpisodePlot:
        """プロット生成コマンド処理

        Args:
            command: プロット生成コマンド

        Returns:
            GeneratedEpisodePlot: 生成されたプロット
        """
        self.logger_service.info(
            "プロット生成開始: command_id=%s",
            command.command_id,
        )

        try:
            # イベント発行: プロット生成開始
            generation_started_event = PlotGenerationStarted(
                command_id=command.command_id,
                episode_number=command.episode_number,
                chapter_title=command.chapter_title,
                generation_mode="ai_enhanced" if command.use_ai_enhancement else "basic"
            )
            self.uow.add_event(generation_started_event)

            # 既存のプロット生成ロジック実行
            plot_result = self._execute_plot_generation(command)

            # 品質チェック実行（オプション）
            quality_score = None
            if command.quality_check:
                quality_score = self._execute_quality_check(plot_result, command)

            # 自動保存実行（オプション）
            file_path = None
            if command.auto_save:
                file_path = self._auto_save_plot(plot_result, command)

            # 生成統計情報の準備
            generation_stats = {
                "generation_time": project_now().to_iso_string(),
                "use_ai_enhancement": command.use_ai_enhancement,
                "quality_check_performed": command.quality_check,
                "auto_save_performed": command.auto_save,
                "episode_number": command.episode_number,
                "target_length": command.target_length
            }

            # イベント発行: プロット生成完了
            generation_completed_event = PlotGenerationCompleted(
                command_id=command.command_id,
                plot_file_path=file_path or "memory_only",
                generation_stats=generation_stats,
                quality_score=quality_score
            )
            self.uow.add_event(generation_completed_event)

            self.logger_service.info(
                "プロット生成完了: command_id=%s",
                command.command_id,
            )
            return plot_result

        except Exception as e:
            # エラーイベント発行
            error_event = PlotGenerationFailed(
                command_id=command.command_id,
                error_message=str(e),
                error_type=type(e).__name__,
                retry_count=0
            )
            self.uow.add_event(error_event)

            self.logger_service.exception(
                "プロット生成エラー: command_id=%s, error=%s",
                command.command_id,
                e,
            )
            raise

    def _execute_plot_generation(self, command: GeneratePlotCommand) -> GeneratedEpisodePlot:
        """実際のプロット生成処理

        Args:
            command: プロット生成コマンド

        Returns:
            GeneratedEpisodePlot: 生成されたプロット
        """
        # ここで既存のPlotGenerationUseCaseのロジックを統合
        # 実装上の簡素化のため、基本的な生成処理を実装

        # 基本的なプロット要素を生成
        title = command.chapter_title or f"第{command.episode_number or 1}話"
        summary = f"Claude生成プロット: {title}"

        # シーン情報の生成
        scenes = [
            {
                "title": "導入シーン",
                "setting": "開始場所",
                "description": "基本状況の設定",
                "duration": "短時間"
            },
            {
                "title": "展開シーン",
                "setting": "メイン場所",
                "description": "主要な出来事の展開",
                "duration": "中時間"
            },
            {
                "title": "解決シーン",
                "setting": "解決場所",
                "description": "問題の解決と次話への布石",
                "duration": "短時間"
            }
        ]

        # キーイベントの生成
        key_events = [
            "状況設定と問題提示",
            "主人公の行動と困難",
            "解決への糸口発見",
            "問題解決と成長"
        ]

        return GeneratedEpisodePlot(
            episode_number=command.episode_number or 1,
            title=title,
            summary=summary,
            scenes=scenes,
            key_events=key_events,
            viewpoint="三人称単元視点",
            tone=command.genre or "成長と発見",
            conflict="技術的困難と内面的成長",
            resolution="協力による問題解決と新たな理解の獲得",
            generation_timestamp=project_now().datetime,
            source_chapter_number=1
        )

    def _execute_quality_check(self, plot: GeneratedEpisodePlot, command: GeneratePlotCommand) -> float:
        """プロット品質チェック実行

        Args:
            plot: 生成されたプロット
            command: 元のコマンド

        Returns:
            float: 品質スコア（0.0-1.0）
        """
        # 基本的な品質チェックロジック
        summary_length = len(plot.summary)
        has_title = bool(plot.title.strip())
        has_scenes = len(plot.scenes) > 0
        has_events = len(plot.key_events) > 0
        has_conflict_resolution = plot.has_conflict_resolution()

        quality_score = 0.0
        if summary_length > 50:
            quality_score += 0.2
        if has_title:
            quality_score += 0.2
        if has_scenes:
            quality_score += 0.2
        if has_events:
            quality_score += 0.2
        if has_conflict_resolution:
            quality_score += 0.2

        return min(quality_score, 1.0)

    def _auto_save_plot(self, plot: GeneratedEpisodePlot, command: GeneratePlotCommand) -> str:
        """プロット自動保存

        Args:
            plot: 生成されたプロット
            command: 元のコマンド

        Returns:
            str: 保存先パス
        """
        project_path = Path(command.project_root)
        episode_num = command.episode_number or 1

        # ファイル名生成
        filename = f"episode_{episode_num:03d}_plot.md"
        file_path = project_path / "20_プロット" / filename

        # ディレクトリ作成
        file_path.parent.mkdir(parents=True, exist_ok=True)

        # プロットをマークダウン形式に変換
        plot_content = f"""# {plot.title}

## 概要
{plot.summary}

## 基本情報
- エピソード番号: {plot.episode_number}
- 視点: {plot.viewpoint}
- トーン: {plot.tone}

## シーン構成
"""

        for i, scene in enumerate(plot.scenes, 1):
            plot_content += f"""
### シーン{i}: {scene.get('title', '未設定')}
- 場所: {scene.get('setting', '未設定')}
- 時間: {scene.get('duration', '未設定')}
- 内容: {scene.get('description', '未設定')}
"""

        plot_content += """
## 主要イベント
"""
        for i, event in enumerate(plot.key_events, 1):
            plot_content += f"{i}. {event}\n"

        plot_content += f"""
## 物語要素
- **コンフリクト**: {plot.conflict}
- **解決**: {plot.resolution}

## 生成情報
- 生成日時: {plot.generation_timestamp.strftime('%Y-%m-%d %H:%M:%S')}
- ソース章: 第{plot.source_chapter_number}章
"""

        # ファイル保存
        file_path.write_text(plot_content.strip(), encoding="utf-8")

        return str(file_path)


class PlotValidationCommandHandler:
    """プロット品質チェックコマンドハンドラー"""

    def __init__(
        self,
        logger_service: "ILoggerService",
        unit_of_work: "IUnitOfWork"
    ) -> None:
        self.logger_service = logger_service
        self.uow = unit_of_work

    def handle(self, command: ValidatePlotCommand) -> dict[str, Any]:
        """プロット品質チェック実行

        Args:
            command: 品質チェックコマンド

        Returns:
            dict[str, Any]: 品質チェック結果
        """
        self.logger_service.info(
            "プロット品質チェック開始: command_id=%s",
            command.command_id,
        )

        try:
            # 品質チェック実行
            quality_metrics = self._validate_plot_quality(command.plot_content, command.validation_criteria)
            quality_score = quality_metrics.get("overall_score", 0.0)
            passed_validation = quality_score >= 0.7  # 70%以上で合格

            # イベント発行
            quality_check_event = PlotQualityCheckCompleted(
                plot_file_path=command.project_root,
                quality_score=quality_score,
                quality_metrics=quality_metrics,
                passed_validation=passed_validation
            )
            self.uow.add_event(quality_check_event)

            return {
                "quality_score": quality_score,
                "passed_validation": passed_validation,
                "metrics": quality_metrics
            }

        except Exception as e:
            self.logger_service.exception(
                "プロット品質チェックエラー: %s",
                e,
            )
            raise

    def _validate_plot_quality(self, content: str, criteria: dict[str, Any]) -> dict[str, Any]:
        """実際の品質チェック処理"""
        metrics = {}

        # 基本メトリクス
        metrics["content_length"] = len(content)
        metrics["has_title"] = "タイトル" in content
        metrics["has_structure"] = "構成" in content or "プロット" in content
        metrics["paragraph_count"] = content.count("\n\n") + 1

        # 総合スコア計算
        score = 0.0
        if metrics["content_length"] > 200:
            score += 0.3
        if metrics["has_title"]:
            score += 0.3
        if metrics["has_structure"]:
            score += 0.2
        if metrics["paragraph_count"] >= 3:
            score += 0.2

        metrics["overall_score"] = min(score, 1.0)

        return metrics


class PlotSaveCommandHandler:
    """プロット保存コマンドハンドラー"""

    def __init__(
        self,
        logger_service: "ILoggerService",
        unit_of_work: "IUnitOfWork"
    ) -> None:
        self.logger_service = logger_service
        self.uow = unit_of_work

    def handle(self, command: SavePlotCommand) -> str:
        """プロット保存処理

        Args:
            command: プロット保存コマンド

        Returns:
            str: 保存先パス
        """
        try:
            file_path = Path(command.file_path)
            backup_path = None

            # バックアップ作成（オプション）
            if command.backup_existing and file_path.exists():
                backup_path = str(file_path.with_suffix(f".backup.{project_now().strftime('%Y%m%d_%H%M%S')}.md"))
                Path(backup_path).write_text(file_path.read_text(encoding="utf-8"), encoding="utf-8")

            # メインファイル保存
            file_path.parent.mkdir(parents=True, exist_ok=True)
            file_path.write_text(command.plot_content, encoding="utf-8")

            # イベント発行
            save_event = PlotSaved(
                file_path=str(file_path),
                backup_path=backup_path,
                metadata=command.metadata
            )
            self.uow.add_event(save_event)

            self.logger_service.info(
                "プロット保存完了: %s",
                file_path,
            )
            return str(file_path)

        except Exception as e:
            self.logger_service.exception(
                "プロット保存エラー: %s",
                e,
            )
            raise


class MasterPlotUpdateCommandHandler:
    """マスタープロット更新コマンドハンドラー"""

    def __init__(
        self,
        logger_service: "ILoggerService",
        unit_of_work: "IUnitOfWork"
    ) -> None:
        self.logger_service = logger_service
        self.uow = unit_of_work

    def handle(self, command: UpdateMasterPlotCommand) -> dict[str, Any]:
        """マスタープロット更新処理

        Args:
            command: マスタープロット更新コマンド

        Returns:
            dict[str, Any]: 更新結果
        """
        try:
            episode_path = Path(command.episode_plot_path)
            master_path = Path(command.master_plot_path)

            # エピソードプロット読み込み
            episode_content = episode_path.read_text(encoding="utf-8")

            # マスタープロット更新
            updated_sections = self._update_master_plot(
                master_path, episode_content, command.integration_mode
            )

            # イベント発行
            update_event = MasterPlotUpdated(
                master_plot_path=str(master_path),
                episode_count=len(updated_sections),
                integration_mode=command.integration_mode,
                updated_sections=updated_sections
            )
            self.uow.add_event(update_event)

            return {
                "master_plot_path": str(master_path),
                "updated_sections": updated_sections,
                "integration_mode": command.integration_mode
            }

        except Exception as e:
            self.logger_service.exception(
                "マスタープロット更新エラー: %s",
                e,
            )
            raise

    def _update_master_plot(self, master_path: Path, episode_content: str, mode: str) -> list[str]:
        """実際のマスタープロット更新処理"""
        updated_sections = []

        if not master_path.exists():
            # マスターファイル新規作成
            master_content = f"# マスタープロット\n\n{episode_content}\n"
            updated_sections.append("new_file_created")
        else:
            # 既存ファイル更新
            existing_content = master_path.read_text(encoding="utf-8")

            if mode == "append":
                master_content = f"{existing_content}\n\n## 追加エピソード\n{episode_content}\n"
                updated_sections.append("appended_episode")
            elif mode == "merge":
                # 簡単なマージ処理
                master_content = f"{existing_content}\n\n## マージされたコンテンツ\n{episode_content}\n"
                updated_sections.append("merged_content")
            elif mode == "replace":
                master_content = episode_content
                updated_sections.append("replaced_content")
            else:
                msg = f"不明な統合モード: {mode}"
                raise ValueError(msg)

        # ファイル保存
        master_path.parent.mkdir(parents=True, exist_ok=True)
        master_path.write_text(master_content, encoding="utf-8")

        return updated_sections
