"""Template use cases."""

from __future__ import annotations

import uuid
from dataclasses import dataclass, field
from typing import List, Optional

from packages.application.template.commands import (
    CreateCategoryCommand,
    CreateTemplateCommand,
    UpdateTemplateCommand,
    ValidateTemplateCommand,
)
from packages.domain.editing_mode import EditingMode
from packages.domain.template import Template, TemplateCategory, TemplateSegment
from packages.ports.template_repository import TemplateRepositoryPort


class NotFoundError(Exception):
    pass


class ValidationError(Exception):
    """业务规则校验失败."""

    pass


VALID_MODES = {m.value for m in EditingMode}
VALID_MATERIAL_TYPES = {"人物", "场景"}


@dataclass
class GenerateWarning:
    """生成时的警告信息."""

    code: str  # voiceover_duration_mismatch / missing_material_type / ...
    message: str
    details: dict = field(default_factory=dict)


@dataclass
class ValidateResult:
    """模板校验结果."""

    template: Template
    warnings: List[GenerateWarning] = field(default_factory=list)


# ── Template CRUD ──


class CreateTemplateUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, command: CreateTemplateCommand) -> Template:
        if command.mode not in VALID_MODES:
            raise ValidationError(f"无效的剪辑模式: {command.mode}，可选值: {VALID_MODES}")

        template_id = uuid.uuid4().hex
        template = Template(
            id=template_id,
            user_id=command.user_id,
            name=command.name,
            mode=command.mode,
            category=command.category,
            tags=command.tags,
            title_config=command.title_config,
            subtitle_config=command.subtitle_config,
            bgm_config=command.bgm_config,
            estimated_duration=command.estimated_duration,
        )
        template = self.repository.create(template)

        # 始终调用 create_segments 以确保在同一事务中提交
        segments = [
            TemplateSegment(
                id=uuid.uuid4().hex,
                template_id=template.id,
                segment_order=seg.segment_order,
                duration_min=seg.duration_min,
                duration_max=seg.duration_max,
                material_type=seg.material_type,
            )
            for seg in command.segments
        ]
        self.repository.create_segments(segments)
        template.segments = segments

        return template


class ListTemplatesUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(
        self,
        user_id: str,
        *,
        skip: int = 0,
        limit: int = 50,
    ) -> List[Template]:
        return self.repository.list_by_user(user_id, skip=skip, limit=limit)


class GetTemplateUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, template_id: str, user_id: str) -> Optional[Template]:
        return self.repository.get(template_id, user_id)


class UpdateTemplateUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, command: UpdateTemplateCommand) -> Template:
        existing = self.repository.get(command.template_id, command.user_id)
        if existing is None:
            raise NotFoundError(f"Template {command.template_id} not found")

        if command.mode is not None and command.mode not in VALID_MODES:
            raise ValidationError(f"无效的剪辑模式: {command.mode}")

        if command.name is not None:
            existing.name = command.name
        if command.mode is not None:
            existing.mode = command.mode
        if command.category is not None:
            existing.category = command.category
        if command.tags is not None:
            existing.tags = command.tags
        if command.title_config is not None:
            existing.title_config = command.title_config
        if command.subtitle_config is not None:
            existing.subtitle_config = command.subtitle_config
        if command.bgm_config is not None:
            existing.bgm_config = command.bgm_config
        if command.estimated_duration is not None:
            existing.estimated_duration = command.estimated_duration

        self.repository.update(existing)

        # Replace segments if provided
        if command.segments is not None:
            self.repository.delete_segments_by_template(existing.id)
            segments = [
                TemplateSegment(
                    id=uuid.uuid4().hex,
                    template_id=existing.id,
                    segment_order=seg.segment_order,
                    duration_min=seg.duration_min,
                    duration_max=seg.duration_max,
                    material_type=seg.material_type,
                )
                for seg in command.segments
            ]
            self.repository.create_segments(segments)
            existing.segments = segments
        else:
            existing.segments = self.repository.list_segments(existing.id)

        return existing


class DeleteTemplateUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, template_id: str, user_id: str) -> bool:
        return self.repository.delete(template_id, user_id)


# ── Validate template ──


class ValidateTemplateUseCase:
    """校验模板业务规则."""

    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, command: ValidateTemplateCommand) -> ValidateResult:
        template = self.repository.get(command.template_id, command.user_id)
        if template is None:
            raise NotFoundError(f"Template {command.template_id} not found")

        warnings: List[GenerateWarning] = []

        # 业务规则 1: one_take 必须恰好 1 个片段
        if template.mode == EditingMode.ONE_TAKE.value:
            if len(template.segments) != 1:
                raise ValidationError(f"一镜到底模式必须恰好有 1 个片段，当前有 {len(template.segments)} 个")

        # 业务规则 2: voice_over 每个片段必须有 material_type
        if template.mode == EditingMode.VOICE_OVER.value:
            for seg in template.segments:
                if not seg.material_type or seg.material_type not in VALID_MATERIAL_TYPES:
                    raise ValidationError(
                        f"口播+B-roll模式下每个片段必须指定 material_type（人物/场景），"
                        f"片段 {seg.segment_order} 的 material_type 无效: {seg.material_type}"
                    )

        # 业务规则 3: 配音时长偏差 ±30% 警告
        if command.voiceover_duration is not None and template.estimated_duration > 0:
            ratio = command.voiceover_duration / template.estimated_duration
            if ratio < 0.7 or ratio > 1.3:
                warnings.append(
                    GenerateWarning(
                        code="voiceover_duration_mismatch",
                        message=(
                            f"配音时长 ({command.voiceover_duration:.1f}s) "
                            f"与预估时长 ({template.estimated_duration:.1f}s) "
                            f"偏差超过 ±30%，可能影响剪辑效果"
                        ),
                        details={
                            "voiceover_duration": command.voiceover_duration,
                            "estimated_duration": template.estimated_duration,
                            "ratio": round(ratio, 3),
                        },
                    )
                )

        return ValidateResult(template=template, warnings=warnings)


# ── Category CRUD ──


class CreateCategoryUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, command: CreateCategoryCommand) -> TemplateCategory:
        category = TemplateCategory(
            id=uuid.uuid4().hex,
            user_id=command.user_id,
            name=command.name,
        )
        return self.repository.create_category(category)


class ListCategoriesUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, user_id: str) -> List[TemplateCategory]:
        return self.repository.list_categories(user_id)


class DeleteCategoryUseCase:
    def __init__(self, repository: TemplateRepositoryPort) -> None:
        self.repository = repository

    def execute(self, category_id: str, user_id: str) -> bool:
        return self.repository.delete_category(category_id, user_id)
