from __future__ import annotations

from dataclasses import dataclass, field
from enum import StrEnum
from itertools import count
from typing import Any


_id_counter = count(1)


def _new_id(prefix: str) -> str:
    return f"{prefix}-{next(_id_counter)}"


class EvidenceSource(StrEnum):
    FINGERPRINT = "fingerprint"
    FACE_PERSON = "face_person"
    WEB_DETECTION = "web_detection"
    NAVER_SEARCH = "naver_search"
    LLM_SUMMARY = "llm_summary"
    EXTERNAL_SKIPPED = "external_skipped"
    SEARCH_SKIPPED = "search_skipped"
    FAILURE = "failure"
    ENRICHMENT_FAILURE = "enrichment_failure"


class KnowledgeEntryType(StrEnum):
    CELEBRITY = "celebrity"
    GROUP = "group"
    WORK = "work"
    CHARACTER = "character"
    WEBTOON = "webtoon"
    GAME = "game"
    REJECTED_IMAGE = "rejected_image"
    OTHER = "other"


class KnowledgeProvenance(StrEnum):
    MANUAL = "manual"
    AUTOMATIC_REJECTION = "automatic_rejection"
    EXTERNAL_EVIDENCE = "external_evidence"


class ReviewStatus(StrEnum):
    APPROVED = "approved"
    HELD = "held"
    REJECTED = "rejected"


class DataClass(StrEnum):
    ORIGINAL_IMAGE = "original_image"
    INTERNAL_DERIVATIVE = "internal_derivative"
    EXTERNAL_DERIVATIVE = "external_derivative"
    IMAGE_FINGERPRINT = "image_fingerprint"
    WEB_EVIDENCE = "web_evidence"
    SEARCH_EVIDENCE = "search_evidence"
    LLM_SUMMARY = "llm_summary"
    PROVIDER_METADATA = "provider_metadata"
    OPERATOR_NOTE = "operator_note"


@dataclass(frozen=True)
class Evidence:
    source: EvidenceSource
    reason: str
    confidence: float
    data: dict[str, Any] = field(default_factory=dict)


@dataclass(frozen=True)
class ScoreResult:
    score: int
    band: str
    reasons: list[str] = field(default_factory=list)


@dataclass
class AnalysisRun:
    id: str
    submission_id: str
    analysis_version: str
    evidence: list[Evidence] = field(default_factory=list)
    score: ScoreResult | None = None

    @classmethod
    def for_submission(cls, submission_id: str, analysis_version: str) -> "AnalysisRun":
        return cls(
            id=_new_id("analysis"),
            submission_id=submission_id,
            analysis_version=analysis_version,
        )

    def add_evidence(self, evidence: Evidence) -> None:
        self.evidence.append(evidence)


@dataclass
class KnowledgeBaseEntry:
    id: str
    entry_type: KnowledgeEntryType
    name: str
    provenance: KnowledgeProvenance
    aliases: list[str] = field(default_factory=list)
    related_keywords: list[str] = field(default_factory=list)
    policy_memo: str = ""
    exception_conditions: str = ""
    sample_fingerprints: list[str] = field(default_factory=list)
    source_decision_id: str | None = None
    entry_status: str = "confirmed"
    source_submission_id: str = ""
    active: bool = True
    deactivation_reason: str | None = None
    data_classes: set[DataClass] = field(default_factory=set)

    @classmethod
    def create(
        cls,
        entry_type: KnowledgeEntryType,
        name: str,
        provenance: KnowledgeProvenance,
        aliases: list[str] | None = None,
        related_keywords: list[str] | None = None,
        policy_memo: str = "",
        exception_conditions: str = "",
        sample_fingerprints: list[str] | None = None,
        source_decision_id: str | None = None,
        entry_status: str = "confirmed",
        source_submission_id: str = "",
    ) -> "KnowledgeBaseEntry":
        data_classes = {DataClass.OPERATOR_NOTE}
        if sample_fingerprints:
            data_classes.add(DataClass.IMAGE_FINGERPRINT)

        return cls(
            id=_new_id("knowledge"),
            entry_type=entry_type,
            name=name,
            provenance=provenance,
            aliases=aliases or [],
            related_keywords=related_keywords or [],
            policy_memo=policy_memo,
            exception_conditions=exception_conditions,
            sample_fingerprints=sample_fingerprints or [],
            source_decision_id=source_decision_id,
            entry_status=entry_status,
            source_submission_id=source_submission_id,
            data_classes=data_classes,
        )


@dataclass
class OperatorDecision:
    id: str
    submission_id: str
    status: ReviewStatus
    memo: str = ""

    @classmethod
    def create(
        cls, submission_id: str, status: ReviewStatus, memo: str = ""
    ) -> "OperatorDecision":
        return cls(
            id=_new_id("decision"),
            submission_id=submission_id,
            status=status,
            memo=memo,
        )


class InMemoryRightsFilterRepository:
    def __init__(self) -> None:
        self._analysis_runs: list[AnalysisRun] = []
        self._knowledge_entries: dict[str, KnowledgeBaseEntry] = {}
        self._operator_decisions: dict[str, OperatorDecision] = {}

    def save_analysis_run(self, run: AnalysisRun) -> None:
        self._analysis_runs.append(run)

    def analysis_runs_for_submission(self, submission_id: str) -> list[AnalysisRun]:
        return [
            run
            for run in self._analysis_runs
            if run.submission_id == submission_id
        ]

    def has_analysis_run(self, submission_id: str, analysis_version: str) -> bool:
        return any(
            run.submission_id == submission_id
            and run.analysis_version == analysis_version
            for run in self._analysis_runs
        )

    def latest_score_for_submission(self, submission_id: str) -> ScoreResult | None:
        for run in reversed(self._analysis_runs):
            if run.submission_id == submission_id and run.score is not None:
                return run.score
        return None

    def save_knowledge_entry(self, entry: KnowledgeBaseEntry) -> None:
        self._knowledge_entries[entry.id] = entry

    def knowledge_entry(self, entry_id: str) -> KnowledgeBaseEntry:
        return self._knowledge_entries[entry_id]

    def active_knowledge_entries(self) -> list[KnowledgeBaseEntry]:
        return [
            entry
            for entry in self._knowledge_entries.values()
            if entry.active
        ]

    def knowledge_entries_for_source_decision(
        self, decision_id: str
    ) -> list[KnowledgeBaseEntry]:
        return [
            entry
            for entry in self._knowledge_entries.values()
            if entry.source_decision_id == decision_id
        ]

    def deactivate_knowledge_entry(self, entry_id: str, reason: str) -> None:
        entry = self.knowledge_entry(entry_id)
        entry.active = False
        entry.deactivation_reason = reason

    def deactivate_entries_for_source_decision(
        self, decision_id: str, reason: str
    ) -> list[KnowledgeBaseEntry]:
        entries = self.knowledge_entries_for_source_decision(decision_id)
        for entry in entries:
            entry.active = False
            entry.deactivation_reason = reason
        return entries

    def save_operator_decision(self, decision: OperatorDecision) -> None:
        self._operator_decisions[decision.id] = decision

    def operator_decision(self, decision_id: str) -> OperatorDecision:
        return self._operator_decisions[decision_id]

    def create_rejected_image_entry(
        self, decision_id: str, submission_id: str, fingerprints: list[str]
    ) -> KnowledgeBaseEntry:
        entry = KnowledgeBaseEntry.create(
            entry_type=KnowledgeEntryType.REJECTED_IMAGE,
            name=f"rejected:{submission_id}",
            provenance=KnowledgeProvenance.AUTOMATIC_REJECTION,
            source_decision_id=decision_id,
            sample_fingerprints=fingerprints,
        )
        self.save_knowledge_entry(entry)
        return entry
