"""
Pydantic schemas for Claude Control Plane.

These schemas define the structure for Claude Code's dynamic configuration,
enabling Django-backed control instead of static markdown files.

SST Flow:
    definitions/user_flows/platform/claude_control_plane.yaml (spec)
        ↓
    pydantic/models/claude.py (this file - validation)
        ↓
    Django models in lightwave-platform (persistence)

Usage in Django models:
    from lightwave.schema.pydantic.models.claude import (
        ClaudeRuleSchema,
        IntentPatternSchema,
        ContextProfileConfigSchema,
    )

    class ClaudeAgent(models.Model):
        intent_patterns = models.JSONField(default=list)

        def clean(self):
            for pattern in self.intent_patterns:
                IntentPatternSchema(**pattern)

        @property
        def intent_patterns_typed(self) -> list[IntentPatternSchema]:
            return [IntentPatternSchema(**p) for p in self.intent_patterns]
"""

from datetime import datetime
from typing import Literal

from pydantic import Field

from lightwave.schema.pydantic.core.base import JSONFieldSchema

# =============================================================================
# CLAUDE RULE SCHEMAS
# =============================================================================


class BranchPatternSchema(JSONFieldSchema):
    """
    Schema for branch patterns that activate rules/profiles.

    Uses fnmatch-style patterns to match git branches.

    Example:
        {
            "pattern": "feature/ui-*",
            "description": "Frontend feature branches"
        }
    """

    pattern: str = Field(..., description="fnmatch pattern (e.g., 'feature/ui-*')")
    description: str = Field("", description="What branches this matches")


class RuleApplicabilitySchema(JSONFieldSchema):
    """
    Schema for ClaudeRule.applies_to_* JSONFields.

    Defines when a rule should be loaded into context.

    Example:
        {
            "branch_patterns": ["feature/ui-*", "fix/frontend-*"],
            "task_types": ["frontend", "component", "ui"],
            "domains": ["cineos", "createos"]
        }
    """

    branch_patterns: list[str] = Field(
        default_factory=list,
        description="fnmatch patterns for branches (empty = all branches)",
    )
    task_types: list[str] = Field(
        default_factory=list,
        description="Task types this rule applies to (empty = all types)",
    )
    domains: list[str] = Field(
        default_factory=list,
        description="Tenant domains this rule applies to (empty = all)",
    )
    exclude_patterns: list[str] = Field(
        default_factory=list,
        description="Patterns to explicitly exclude",
    )


# =============================================================================
# CLAUDE AGENT SCHEMAS
# =============================================================================


class IntentPatternSchema(JSONFieldSchema):
    """
    Schema for agent intent patterns stored in ClaudeAgent.intent_patterns.

    Patterns are regex strings matched against user prompts to determine
    which agent should handle the request.

    Example:
        {
            "pattern": "\\b(react|component|frontend|ui)\\b",
            "weight": 1.0,
            "description": "Matches frontend-related terms"
        }
    """

    pattern: str = Field(..., description="Regex pattern to match against prompts")
    weight: float = Field(1.0, ge=0.0, le=2.0, description="Weight for confidence calculation")
    description: str = Field("", description="What this pattern matches")
    case_sensitive: bool = Field(False, description="Whether pattern is case-sensitive")


class AgentToolConfigSchema(JSONFieldSchema):
    """
    Schema for agent tool restrictions in ClaudeAgent.tools_config.

    Defines which tools an agent can use and any restrictions.

    Example:
        {
            "allowed": ["Read", "Glob", "Grep", "Edit"],
            "denied": ["Bash"],
            "require_confirmation": ["Write", "Edit"]
        }
    """

    allowed: list[str] = Field(
        default_factory=list,
        description="Tools this agent can use (empty = all tools)",
    )
    denied: list[str] = Field(
        default_factory=list,
        description="Tools explicitly denied to this agent",
    )
    require_confirmation: list[str] = Field(
        default_factory=list,
        description="Tools that require user confirmation before use",
    )


class AgentMetricsSchema(JSONFieldSchema):
    """
    Schema for agent performance metrics in ClaudeAgent.metrics.

    Tracks how well an agent performs for analytics and learning.

    Example:
        {
            "invocation_count": 150,
            "success_count": 135,
            "failure_count": 10,
            "abandoned_count": 5,
            "avg_duration_seconds": 180.5,
            "avg_tokens_used": 25000
        }
    """

    invocation_count: int = Field(0, ge=0, description="Total times agent was invoked")
    success_count: int = Field(0, ge=0, description="Successful completions")
    failure_count: int = Field(0, ge=0, description="Failed sessions")
    abandoned_count: int = Field(0, ge=0, description="User abandoned sessions")
    avg_duration_seconds: float = Field(0.0, ge=0.0, description="Average session duration")
    avg_tokens_used: int = Field(0, ge=0, description="Average tokens per session")

    @property
    def success_rate(self) -> float:
        """Calculate success rate as percentage."""
        total = self.success_count + self.failure_count + self.abandoned_count
        if total == 0:
            return 0.0
        return self.success_count / total


# =============================================================================
# KNOWLEDGE ARTICLE SCHEMAS
# =============================================================================


SourceType = Literal["package", "codebase", "manual", "external"]


class KnowledgeSourceSchema(JSONFieldSchema):
    """
    Schema for KnowledgeArticle source tracking.

    Tracks where knowledge came from for staleness detection.

    Example:
        {
            "type": "package",
            "reference": "django-ninja",
            "version_tracked": "1.3.0",
            "current_version": "1.4.0",
            "last_checked": "2025-01-22T10:30:00Z"
        }
    """

    type: SourceType = Field(..., description="Type of knowledge source")
    reference: str = Field(..., description="Package name, file path, or URL")
    version_tracked: str | None = Field(None, description="Version this doc was written for")
    current_version: str | None = Field(None, description="Currently installed/available version")
    last_checked: datetime | None = Field(None, description="When version was last checked")


class StalenessInfoSchema(JSONFieldSchema):
    """
    Schema for KnowledgeArticle staleness information.

    Tracks why and when an article became stale.

    Example:
        {
            "is_stale": true,
            "reason": "Package updated from 2.5.0 to 2.6.0 with breaking changes",
            "detected_at": "2025-01-22T10:30:00Z",
            "breaking_changes": true,
            "changelog_url": "https://github.com/pydantic/pydantic/releases/tag/v2.6.0"
        }
    """

    is_stale: bool = Field(False, description="Whether this article needs updating")
    reason: str = Field("", description="Why the article is stale")
    detected_at: datetime | None = Field(None, description="When staleness was detected")
    breaking_changes: bool = Field(False, description="Whether update includes breaking changes")
    changelog_url: str = Field("", description="Link to changelog if available")


# =============================================================================
# CONTEXT PROFILE SCHEMAS
# =============================================================================


class ContextProfileConfigSchema(JSONFieldSchema):
    """
    Schema for ContextProfile configuration.

    Defines what context to load for different development scenarios.

    Example:
        {
            "branch_patterns": ["feature/ui-*", "fix/frontend-*"],
            "task_types": ["frontend", "component"],
            "knowledge_topics": ["react", "vite", "tailwind"],
            "max_context_tokens": 60000,
            "priority": 10
        }
    """

    branch_patterns: list[str] = Field(
        default_factory=list,
        description="Git branch patterns that activate this profile",
    )
    task_types: list[str] = Field(
        default_factory=list,
        description="Task types that activate this profile",
    )
    knowledge_topics: list[str] = Field(
        default_factory=list,
        description="Knowledge article topics to include",
    )
    max_context_tokens: int = Field(
        50000,
        ge=1000,
        le=200000,
        description="Maximum context size for this profile",
    )
    priority: int = Field(0, description="Priority when multiple profiles match")


# =============================================================================
# PACKAGE STATE SCHEMAS
# =============================================================================


Ecosystem = Literal["python", "npm", "system"]


class PackageVersionSchema(JSONFieldSchema):
    """
    Schema for PackageState version tracking.

    Tracks locked, installed, and latest versions.

    Example:
        {
            "locked": "5.0.1",
            "installed": "5.0.1",
            "latest": "5.0.3",
            "has_drift": false,
            "has_security_advisory": false
        }
    """

    locked: str = Field(..., description="Version from lock file")
    installed: str | None = Field(None, description="Actually installed version")
    latest: str | None = Field(None, description="Latest available version")

    @property
    def has_drift(self) -> bool:
        """Check if installed version differs from locked."""
        if self.installed is None:
            return False
        return self.installed != self.locked

    @property
    def has_update_available(self) -> bool:
        """Check if a newer version is available."""
        if self.latest is None:
            return False
        return self.latest != self.locked


class SecurityAdvisorySchema(JSONFieldSchema):
    """
    Schema for security advisories in PackageState.advisories.

    Example:
        {
            "id": "GHSA-xxxx-xxxx-xxxx",
            "severity": "high",
            "title": "SQL Injection vulnerability",
            "patched_versions": [">=5.0.2"],
            "url": "https://github.com/advisories/..."
        }
    """

    id: str = Field(..., description="Advisory ID (e.g., GHSA-xxxx)")
    severity: Literal["low", "moderate", "high", "critical"] = Field(..., description="Severity level")
    title: str = Field(..., description="Brief description")
    patched_versions: list[str] = Field(
        default_factory=list,
        description="Version constraints that fix this issue",
    )
    url: str = Field("", description="Link to advisory details")


# =============================================================================
# SESSION TRACKING SCHEMAS
# =============================================================================


SessionOutcome = Literal["success", "partial", "failed", "abandoned"]


class ToolInvocationSchema(JSONFieldSchema):
    """
    Schema for tool usage tracking in ClaudeSession.tools_invoked.

    Example:
        {
            "tool": "Bash",
            "count": 15,
            "success_count": 14,
            "avg_duration_ms": 250
        }
    """

    tool: str = Field(..., description="Tool name")
    count: int = Field(0, ge=0, description="Number of invocations")
    success_count: int = Field(0, ge=0, description="Successful invocations")
    avg_duration_ms: float = Field(0.0, ge=0.0, description="Average duration in milliseconds")


class SessionSummarySchema(JSONFieldSchema):
    """
    Schema for ClaudeSession summary data.

    Captures key metrics and context for a Claude Code session.

    Example:
        {
            "branch": "feature/dark-mode",
            "task_id": "abc12345",
            "profile_used": "frontend",
            "agents_used": ["django-frontend-architect"],
            "outcome": "success",
            "tokens_used": 45000,
            "duration_seconds": 300
        }
    """

    branch: str = Field("", description="Git branch during session")
    task_id: str | None = Field(None, description="Task ID if on task branch")
    profile_used: str = Field("default", description="Context profile that was matched")
    agents_used: list[str] = Field(default_factory=list, description="Agents invoked")
    outcome: SessionOutcome | None = Field(None, description="Session outcome")
    tokens_used: int = Field(0, ge=0, description="Total tokens consumed")
    duration_seconds: float = Field(0.0, ge=0.0, description="Session duration")
    tools_invoked: list[ToolInvocationSchema] = Field(
        default_factory=list,
        description="Tools used during session",
    )
    feedback: str = Field("", description="User feedback on session")


# =============================================================================
# API REQUEST/RESPONSE SCHEMAS
# =============================================================================


class ContextRequestSchema(JSONFieldSchema):
    """
    Schema for POST /api/claude/context/ request body.

    Called by inject-context.sh hook on every prompt.
    """

    branch: str = Field("", description="Current git branch")
    task_id: str | None = Field(None, description="8-char task ID if on task branch")
    session_id: str | None = Field(None, description="Claude session ID for tracking")


class ContextResponseSchema(JSONFieldSchema):
    """
    Schema for POST /api/claude/context/ response.

    Returns rules, knowledge, and context for Claude.
    """

    class RuleItem(JSONFieldSchema):
        slug: str
        content: str
        priority: int

    class KnowledgeItem(JSONFieldSchema):
        topic: str
        title: str
        content: str

    class TaskContext(JSONFieldSchema):
        id: str
        title: str
        description: str
        status: str

    class SprintContext(JSONFieldSchema):
        id: str
        name: str
        goals: list[str]

    rules: list[RuleItem] = Field(default_factory=list)
    knowledge: list[KnowledgeItem] = Field(default_factory=list)
    task_context: TaskContext | None = None
    sprint_context: SprintContext | None = None
    profile_name: str = "default"


class AgentSuggestionRequestSchema(JSONFieldSchema):
    """
    Schema for POST /api/claude/agent/suggest/ request body.

    Called by suggest-routing.sh hook.
    """

    prompt: str = Field(..., description="User's prompt text")
    branch: str | None = Field(None, description="Current git branch")
    task_id: str | None = Field(None, description="Task ID if available")


class AgentSuggestionResponseSchema(JSONFieldSchema):
    """
    Schema for POST /api/claude/agent/suggest/ response.
    """

    class AgentMatch(JSONFieldSchema):
        name: str
        display_name: str
        description: str
        confidence: float = Field(ge=0.0, le=1.0)

    agent: AgentMatch | None = None
    alternatives: list[AgentMatch] = Field(default_factory=list)
