"""
Preflights Core Types.

All types are immutable dataclasses (frozen=True).
No I/O, no external dependencies.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Literal

# =============================================================================
# INPUT TYPES
# =============================================================================


@dataclass(frozen=True)
class Intention:
    """User's intention for the preflight."""

    text: str
    optional_context: str | None = None


@dataclass(frozen=True)
class Question:
    """A clarification question."""

    id: str
    type: Literal["single_choice", "multi_choice", "free_text"]
    question: str
    options: tuple[str, ...] | None = None
    min_selections: int | None = None
    max_selections: int | None = None
    optional: bool = False


@dataclass(frozen=True)
class Answer:
    """An answer to a question."""

    question_id: str
    value: str | tuple[str, ...]  # str for single/free, tuple for multi


@dataclass(frozen=True)
class ConversationState:
    """State of the conversation (questions asked and answers given)."""

    asked_questions: tuple[Question, ...]
    answers: tuple[Answer, ...]  # Use tuple for ordering, not frozenset


@dataclass(frozen=True)
class FileContext:
    """Repository file topology summary."""

    paths: tuple[str, ...]
    high_level_dirs: tuple[str, ...] = ()
    signals: tuple[tuple[str, str], ...] = ()  # (key, value) pairs


@dataclass(frozen=True)
class DecisionPatch:
    """A structured patch to apply to the architecture snapshot."""

    category: str
    fields: tuple[tuple[str, str], ...]  # (field_key, value) pairs
    references: tuple[tuple[str, str], ...] = ()  # (field_key, adr_uid) pairs


# =============================================================================
# SCHEMA & CONFIG TYPES
# =============================================================================


@dataclass(frozen=True)
class SnapshotSchema:
    """
    Fixed schema V1.

    Defines valid categories and their fields.
    """

    categories: tuple[tuple[str, tuple[str, ...]], ...]  # (category, (fields...))


@dataclass(frozen=True)
class HeuristicsConfig:
    """Configuration for heuristics (injected, not hardcoded)."""

    schema: SnapshotSchema
    category_keywords: tuple[
        tuple[str, tuple[str, ...]], ...
    ]  # (category, (keywords...))
    non_triggers: tuple[str, ...] = ("bugfix", "typo", "refactor")
    file_impact_threshold: int = 10


# =============================================================================
# ARCHITECTURE STATE
# =============================================================================


@dataclass(frozen=True)
class ArchitectureState:
    """
    Current architecture snapshot.

    Each category contains field-value pairs.
    """

    uid: str | None  # None if no ADR exists yet
    schema_version: str
    categories: tuple[
        tuple[str, tuple[tuple[str, str], ...]], ...
    ]  # (cat, ((field, value)...))


# =============================================================================
# OUTPUT TYPES (ADR, Task)
# =============================================================================


@dataclass(frozen=True)
class ADR:
    """Architecture Decision Record."""

    uid: str
    title: str
    category: str
    date_utc: str
    previous_uid: str | None
    snapshot: ArchitectureState
    changes_in_this_version: tuple[str, ...]
    context: str
    decision: str
    rationale: str
    alternatives: tuple[str, ...]
    consequences_positive: tuple[str, ...]
    consequences_negative: tuple[str, ...]
    consequences_neutral: tuple[str, ...]
    supersedes_uid: str | None = None


@dataclass(frozen=True)
class Task:
    """Execution brief (CURRENT_TASK)."""

    uid: str
    title: str
    objective: str
    context: str
    allowlist: tuple[str, ...]
    forbidden: tuple[str, ...]
    technical_constraints: tuple[str, ...]
    acceptance_criteria: tuple[str, ...]
    created_at_utc: str
    related_adr_uid: str | None = None


# =============================================================================
# RESULT TYPES (Union fermée à 4 états)
# =============================================================================


@dataclass(frozen=True)
class NeedsClarification:
    """
    Core needs more information.

    Questions are generated by Clarifier (templates based on heuristics).
    """

    questions: tuple[Question, ...]


@dataclass(frozen=True)
class ReadyToBuild:
    """
    Core validated everything. Ready to produce artifacts.

    Application must call process() again with:
    - uid_for_adr (if needs_adr is True)
    - uid_for_task
    - now_utc
    """

    needs_adr: bool
    category: str | None  # If needs_adr, which category
    title: str  # Suggested title for ADR/Task
    # Additional context for building
    allowlist: tuple[str, ...] = ()
    forbidden: tuple[str, ...] = ()
    technical_constraints: tuple[str, ...] = ()
    acceptance_criteria: tuple[str, ...] = ()
    objective: str = ""
    context_text: str = ""
    # ADR-specific fields (if needs_adr)
    decision_context: str = ""
    decision_text: str = ""
    rationale: str = ""
    alternatives: tuple[str, ...] = ()
    consequences_positive: tuple[str, ...] = ()
    consequences_negative: tuple[str, ...] = ()
    consequences_neutral: tuple[str, ...] = ()


@dataclass(frozen=True)
class Completed:
    """Artifacts produced successfully."""

    task: Task
    adr: ADR | None
    updated_architecture: ArchitectureState | None


@dataclass(frozen=True)
class CoreError:
    """Validation failed or state corrupted."""

    code: str
    message: str
    details: tuple[tuple[str, str], ...] = ()  # (key, value) pairs
    recovery_hint: str | None = None


# Union fermée (4 états)
ProcessResult = NeedsClarification | ReadyToBuild | Completed | CoreError


# =============================================================================
# ERROR CODES (aligned with MCP_SPEC)
# =============================================================================


class ErrorCode:
    """Error codes aligned with MCP_SPEC."""

    VALIDATION_FAILED = "VALIDATION_FAILED"
    PARSE_ERROR = "PARSE_ERROR"
    STATE_CORRUPTION = "STATE_CORRUPTION"
    UNKNOWN_CATEGORY = "UNKNOWN_CATEGORY"
    UNKNOWN_FIELD = "UNKNOWN_FIELD"
    ALLOWLIST_UNDETERMINED = "ALLOWLIST_UNDETERMINED"
    EMPTY_ALLOWLIST = "EMPTY_ALLOWLIST"
    FORBIDDEN_OVERLAP = "FORBIDDEN_OVERLAP"
    EMPTY_ACCEPTANCE_CRITERIA = "EMPTY_ACCEPTANCE_CRITERIA"
    INVALID_UID_FORMAT = "INVALID_UID_FORMAT"
    MISSING_REQUIRED_FIELD = "MISSING_REQUIRED_FIELD"


# =============================================================================
# HELPER: Default V1 Schema
# =============================================================================


def default_v1_schema() -> SnapshotSchema:
    """
    Return the default V1 snapshot schema.

    Categories and fields based on CORE_SPEC.

    Note: "Other" is a catch-all category with wildcard "*" that accepts
    any field name. This allows documenting edge cases that don't fit
    standard categories (e.g., "Supabase" which is auth + database).
    """
    return SnapshotSchema(
        categories=(
            ("Frontend", ("Framework", "Routing", "Styling", "UI_Library", "State_Management")),
            ("Backend", ("Language", "Framework", "API_Style", "Validation")),
            ("Database", ("Type", "ORM", "Migrations")),
            ("Authentication", ("Strategy", "Library", "Token_Storage")),
            ("Infra", ("Hosting", "CI_CD", "Observability", "Caching")),
            ("Other", ("*",)),  # Catch-all: accepts any field name
        )
    )


def default_v1_heuristics() -> HeuristicsConfig:
    """
    Return the default V1 heuristics configuration.

    Keywords and triggers based on MCP_SPEC.
    """
    return HeuristicsConfig(
        schema=default_v1_schema(),
        category_keywords=(
            ("Authentication", ("auth", "login", "oauth", "jwt", "session", "password")),
            ("Database", ("database", "db", "postgres", "mysql", "mongo", "redis", "storage")),
            ("Backend", ("api", "endpoint", "server", "backend", "rest", "graphql")),
            ("Frontend", ("ui", "frontend", "react", "vue", "angular", "component")),
            ("Infra", ("deploy", "infrastructure", "ci", "cd", "docker", "kubernetes", "cache")),
        ),
        non_triggers=("bugfix", "typo", "refactor", "fix", "bug"),
        file_impact_threshold=10,
    )
