"""Colony data models — proposals, intelligence briefs, votes, config."""

from __future__ import annotations

from datetime import datetime, timezone
from enum import StrEnum
from typing import Literal

from pydantic import BaseModel, Field


def _now() -> datetime:
    return datetime.now(timezone.utc)


def _prop_id() -> str:
    from uuid import uuid4

    return f"PROP-{uuid4().hex[:6].upper()}"


def _intel_id() -> str:
    from uuid import uuid4

    return f"INTEL-{uuid4().hex[:6].upper()}"


# ── Enums ──────────────────────────────────────────────────────────────


class AgentRole(StrEnum):
    INTELLIGENCE = "intelligence"
    RESEARCH = "research"
    SCOUT = "scout"
    QA = "qa"
    GOVERNANCE = "governance"


class ProposalType(StrEnum):
    MODEL_CONFIG = "model-config"
    NEW_SKILL = "new-skill"
    SKILL_UPDATE = "skill-update"
    ARCHITECTURE_CHANGE = "architecture-change"
    DEPENDENCY_UPDATE = "dependency-update"
    REFACTOR = "refactor"
    NEW_PLAYBOOK = "new-playbook"
    CONFIG_CHANGE = "config-change"
    BUG_FIX = "bug-fix"
    REVERT = "revert"


class ProposalStatus(StrEnum):
    DRAFT = "draft"
    PROPOSED = "proposed"
    UNDER_REVIEW = "under_review"
    APPROVED = "approved"
    REJECTED = "rejected"
    NEEDS_REVISION = "needs_revision"
    ESCALATED = "escalated"
    IMPLEMENTED = "implemented"
    REVERTED = "reverted"


class RiskLevel(StrEnum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    CRITICAL = "critical"


class VotePosition(StrEnum):
    SUPPORT = "support"
    OPPOSE = "oppose"
    SUPPORT_WITH_CONDITIONS = "support-with-conditions"
    ABSTAIN = "abstain"


# ── Core Models ────────────────────────────────────────────────────────


class Evidence(BaseModel):
    source: str
    url: str = ""
    finding: str


class FileChange(BaseModel):
    path: str
    action: Literal["create", "modify", "delete"]
    description: str = ""


class Proposal(BaseModel):
    id: str = Field(default_factory=_prop_id)
    title: str
    type: ProposalType
    risk: RiskLevel
    proposer: AgentRole
    status: ProposalStatus = ProposalStatus.DRAFT
    description: str = ""
    motivation: str = ""
    evidence: list[Evidence] = Field(default_factory=list)
    files_affected: list[FileChange] = Field(default_factory=list)
    rollback_plan: str = ""
    parent_proposal: str | None = None
    children: list[str] = Field(default_factory=list)
    created_at: datetime = Field(default_factory=_now)
    updated_at: datetime = Field(default_factory=_now)


class Vote(BaseModel):
    proposal_id: str
    voter: AgentRole
    position: VotePosition
    reasoning: str
    conditions: list[str] = Field(default_factory=list)
    timestamp: datetime = Field(default_factory=_now)


class GovernanceDecision(BaseModel):
    proposal_id: str
    outcome: ProposalStatus  # approved, rejected, needs_revision, escalated
    reasoning: str
    conditions: list[str] = Field(default_factory=list)
    escalated: bool = False
    escalation_reason: str = ""
    commit_sha: str = ""
    timestamp: datetime = Field(default_factory=_now)


class IntelBrief(BaseModel):
    id: str = Field(default_factory=_intel_id)
    title: str
    source_type: str  # arxiv, blog, hn, release, framework, benchmark
    source_url: str = ""
    category: str = ""  # model-update, framework-release, architecture-pattern, etc.
    relevance: Literal["direct", "indirect", "watch-list"] = "indirect"
    summary: str = ""
    implications: list[str] = Field(default_factory=list)
    connections: list[str] = Field(default_factory=list)
    tags: list[str] = Field(default_factory=list)
    created_at: datetime = Field(default_factory=_now)


class AgentReflection(BaseModel):
    agent: AgentRole
    cycle_number: int
    observation: str
    action_taken: str
    outcome: str
    lesson: str = ""
    timestamp: datetime = Field(default_factory=_now)


class QAMetrics(BaseModel):
    timestamp: datetime = Field(default_factory=_now)
    test_count: int = 0
    pass_count: int = 0
    fail_count: int = 0
    lint_issues: int = 0
    proposal_id: str = ""  # if monitoring a specific proposal


class AgentTokenUsage(BaseModel):
    agent: str
    cycle_number: int
    input_tokens: int = 0
    output_tokens: int = 0
    timestamp: datetime = Field(default_factory=_now)


class TimelineEntry(BaseModel):
    time: datetime = Field(default_factory=_now)
    agent: str
    action: str
    detail: str = ""


# ── Configuration ──────────────────────────────────────────────────────


class ColonyConfig(BaseModel):
    """Colony runtime configuration."""

    experiment_duration_hours: float = 24.0
    code_freeze_at_hour: float = 22.0
    cooldowns: dict[str, int] = Field(
        default_factory=lambda: {
            "intelligence": 1200,  # 20 min in seconds
            "research": 900,  # 15 min
            "scout": 900,  # 15 min
            "qa": 600,  # 10 min
        }
    )
    max_iterations_per_cycle: int = 50
    telegram_chat_id: str = ""
    llm_model: str = ""  # override for colony agents

    # Directed mode (--mission flag)
    mission: str = ""  # Empty = auto mode, non-empty = directed mode
    directed_cooldowns: dict[str, int] = Field(
        default_factory=lambda: {
            "intelligence": 600,  # 10 min (vs 20 min auto)
            "research": 300,  # 5 min (vs 15 min auto)
            "scout": 300,  # 5 min (vs 15 min auto)
            "qa": 300,  # 5 min (vs 10 min auto)
        }
    )
    directed_max_iterations: int = 75  # vs 50 for auto


class ColonyState(BaseModel):
    """Runtime state of the colony experiment."""

    experiment_id: int
    branch_name: str
    started_at: datetime = Field(default_factory=_now)
    status: Literal["bootstrapping", "running", "code_freeze", "stopped"] = "bootstrapping"
    cycle_count: int = 0
    mission: str = ""
    proposals_total: int = 0
    proposals_approved: int = 0
    proposals_rejected: int = 0
