from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, Field


class SignupIn(BaseModel):
    username: str = Field(pattern=r"^[a-zA-Z0-9][a-zA-Z0-9_-]{2,31}$")
    email: str = Field(
        min_length=3,
        max_length=254,
        pattern=r"^[^@\s]+@[^@\s]+\.[^@\s]+$",
    )
    password: str = Field(min_length=8, max_length=200)


class LoginIn(BaseModel):
    username: str = Field(min_length=1, max_length=100)
    password: str = Field(min_length=1, max_length=200)


class ProjectIn(BaseModel):
    slug: str = Field(pattern=r"^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$")
    name: str = Field(min_length=1, max_length=100)
    description: str = Field(default="", max_length=1000)
    repository_url: str = Field(
        max_length=1000,
        pattern=r"^https://github\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/?$",
    )
    spec_path: str = Field(
        default="specs/polyform.toml",
        max_length=1000,
        pattern=r"^[A-Za-z0-9_-]+(?:/[A-Za-z0-9_.-]+)*\.toml$",
    )


class ProjectUpdateIn(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    description: str = Field(default="", max_length=1000)
    repository_url: str = Field(
        max_length=1000,
        pattern=r"^https://github\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+/?$",
    )
    spec_path: str = Field(
        default="specs/polyform.toml",
        max_length=1000,
        pattern=r"^[A-Za-z0-9_-]+(?:/[A-Za-z0-9_.-]+)*\.toml$",
    )


class ApiTokenIn(BaseModel):
    label: str = Field(default="Polyform CLI", min_length=1, max_length=100)


class ImplementationIn(BaseModel):
    id: str = Field(pattern=r"^[A-Za-z0-9_.-]+$")
    name: str
    provenance: dict = Field(default_factory=dict)


class SpecFunctionIn(BaseModel):
    name: str = Field(pattern=r"^[A-Za-z_][A-Za-z0-9_]*$")
    implementations: list[ImplementationIn] = Field(min_length=1)


class ReleaseIn(BaseModel):
    application: str
    version: str
    spec_functions: list[SpecFunctionIn] = Field(min_length=1)
    repository_url: str | None = Field(default=None, max_length=1000)
    source_commit: str | None = Field(default=None, max_length=128)
    spec_path: str | None = Field(default=None, max_length=1000)
    artifact_url: str | None = Field(default=None, max_length=2000)
    artifact_name: str | None = Field(default=None, max_length=255)
    artifact_sha256: str | None = Field(default=None, pattern=r"^[a-fA-F0-9]{64}$")
    evidence_sha256: str | None = Field(default=None, pattern=r"^[a-fA-F0-9]{64}$")
    signing_public_key: str | None = Field(default=None, max_length=200)
    signature: str | None = Field(default=None, max_length=200)
    channel: str = Field(default="stable", pattern=r"^[A-Za-z0-9_.-]+$", max_length=64)


class ClientIn(BaseModel):
    release_id: int
    installation_id: str | None = Field(default=None, max_length=200)
    strategy: Literal["balanced", "random", "homogeneous"] = "balanced"


class CallIn(BaseModel):
    spec_function: str = Field(min_length=1, max_length=128)
    implementation_id: str = Field(min_length=1, max_length=128)
    outcome: Literal["ok", "error", "timeout", "panic"] = "ok"
    duration_ms: float | None = Field(default=None, ge=0)


class TelemetryCallIn(BaseModel):
    spec_function: str = Field(min_length=1, max_length=128)
    implementation_id: str = Field(min_length=1, max_length=128)
    outcome: Literal["ok", "error", "timeout", "panic"]


class ExecutionIn(BaseModel):
    client_id: str = Field(min_length=1, max_length=128)
    composition_revision: int = Field(ge=1)
    operation: str = Field(max_length=100, pattern=r"^[A-Za-z0-9_.:-]+$")
    success: bool
    duration_ms: float | None = Field(default=None, ge=0)
    error_kind: str | None = Field(default=None, max_length=100, pattern=r"^[A-Za-z0-9_.:-]+$")
    calls: list[CallIn] = Field(default_factory=list, max_length=100)


class TelemetryIn(BaseModel):
    client_id: str = Field(min_length=1, max_length=128)
    release_id: str = Field(min_length=1, max_length=128)
    composition_revision: int = Field(ge=1)
    event_type: Literal[
        "panic", "spec_error", "contract_violation", "timeout",
        "integrity_failure", "extraction_failure"
    ]
    spec_function: str = Field(min_length=1, max_length=128)
    implementation_id: str = Field(min_length=1, max_length=128)
    execution_id: int | None = Field(default=None, ge=1)
    message_code: str | None = Field(default=None, max_length=128, pattern=r"^[A-Za-z0-9_.:-]+$")
    recent_calls: list[TelemetryCallIn] = Field(default_factory=list, max_length=32)


class StatusIn(BaseModel):
    status: Literal["active", "quarantined", "disabled"]
    reason: str = Field(default="", max_length=500)
