from __future__ import annotations

from typing import Literal, Protocol
from datetime import datetime

from pydantic import BaseModel, Field, field_validator
from device_gateway.display_text import device_final_text


class Project(BaseModel):
    id: str = Field(pattern=r"^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$")
    name: str = Field(min_length=1, max_length=160)
    path: str = Field(min_length=1, max_length=1024)


class AgentSession(BaseModel):
    agent: str
    session_id: str
    project_id: str
    name: str
    cwd: str
    state: Literal["DORMANT", "IDLE", "RUNNING", "DONE", "FAILED", "OFFLINE", "WAITING_APPROVAL"] = "DORMANT"
    updated_at: str
    model: str | None = None
    effort: str | None = None
    elapsed_seconds: int = 0
    live: bool = False
    latest_activity: str = ""
    recent_text: str = ""
    final_text: str = ""
    activity_type: str | None = None
    waiting_reason: str | None = None

    @field_validator("final_text")
    @classmethod
    def bounded_final(cls, value: str) -> str:
        return device_final_text(value)


class Input(BaseModel):
    text: str = Field(min_length=1, max_length=32000)

    @field_validator("text")
    @classmethod
    def nonblank(cls, value: str) -> str:
        if not value.strip():
            raise ValueError("input cannot be blank")
        return value


class Activity(BaseModel):
    agent: str
    session_id: str
    kind: Literal["text", "tool", "state"]
    text: str


class Approval(BaseModel):
    approval_id: str
    agent: str
    session_id: str
    project_id: str
    kind: Literal["command", "file_change", "tool", "action"]
    title: str
    detail: str
    choices: list[str] = Field(default_factory=lambda: ["approve", "reject"])
    created_at: datetime
    expires_at: datetime | None = None
    timeout_policy: Literal["wait"] = Field(default="wait", exclude=True)
    state: Literal["PENDING", "APPROVED", "REJECTED", "EXPIRED", "CANCELLED"] = "PENDING"
    owner: str = Field(exclude=True)
    request_identity: str = Field(exclude=True)


class Settings(BaseModel):
    model: str | None = None
    effort: str | None = None
    model_config = {"extra": "forbid"}


class ControlError(Exception):
    def __init__(self, detail: str, status: int = 503):
        self.detail, self.status = detail, status
        super().__init__(detail)


class AgentAdapter(Protocol):
    agent_type: str

    async def list_sessions(self, project: Project) -> list[AgentSession]: ...
    async def get_session(self, session_id: str) -> AgentSession: ...
    async def resume_session(self, session_id: str) -> bool: ...
    async def send_input(self, session_id: str, text: str) -> dict: ...
    async def get_settings(self, session_id: str) -> dict: ...
    async def list_models(self, session_id: str) -> list[dict]: ...
    async def update_settings(self, session_id: str, value: Settings) -> dict: ...
    async def close(self) -> None: ...
