import uuid
from datetime import datetime

from pydantic import BaseModel, ConfigDict, EmailStr, Field

# ---------- auth ----------


class RegisterIn(BaseModel):
    nickname: str = Field(min_length=1, max_length=80)
    email: EmailStr
    password: str = Field(min_length=8, max_length=128)


class LoginIn(BaseModel):
    email: EmailStr
    password: str = Field(min_length=1, max_length=128)


class TokenOut(BaseModel):
    token: str
    expires_at: datetime


class UserOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    email: str
    nickname: str
    created_at: datetime


# ---------- projects ----------


class ProjectCreateIn(BaseModel):
    name: str = Field(min_length=1, max_length=120)
    description: str | None = Field(default=None, max_length=2000)


class ProjectUpdateIn(BaseModel):
    name: str | None = Field(default=None, min_length=1, max_length=120)
    description: str | None = Field(default=None, max_length=2000)


class ProjectOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    name: str
    description: str | None
    share_token: str
    created_at: datetime


class ProjectReportCounts(BaseModel):
    total: int
    open: int


class ProjectDetailOut(ProjectOut):
    owner_nickname: str
    report_counts: ProjectReportCounts


# ---------- reports ----------


class ElementContextIn(BaseModel):
    selector: str | None = None
    unique_selector: str | None = None
    tag: str | None = None
    id: str | None = None
    classes: list[str] | None = None
    text_snippet: str | None = None
    aria_label: str | None = None
    test_attributes: dict[str, str] | None = None
    rect: dict | None = None  # {x, y, w, h} viewport px
    screenshot_rel: dict | None = None  # {x, y, w, h} fractions of the screenshot
    extra: dict | None = None


class EnvironmentIn(BaseModel):
    user_agent: str | None = None
    browser: str | None = None
    browser_version: str | None = None
    os: str | None = None
    viewport: dict | None = None  # {w, h}
    dpr: float | None = None
    language: str | None = None
    captured_at: datetime | None = None
    extra: dict | None = None


class MousePointIn(BaseModel):
    t: int = Field(ge=0)  # ms since recording start
    x: float
    y: float


class MouseTrackIn(BaseModel):
    """Sampled cursor positions, viewport-relative CSS px at record time."""
    viewport: dict  # {w, h} of the viewport the points were captured in
    points: list[MousePointIn] = Field(default_factory=list, max_length=5000)


class StepIn(BaseModel):
    type: str = Field(pattern="^(click|input|url_change|navigation|note|screenshot|console)$")
    offset_ms: int = Field(ge=0)
    data: dict = {}
    attachment_id: uuid.UUID | None = None  # uploaded screenshot to link to this step


class ReportCreateIn(BaseModel):
    project_id: uuid.UUID
    type: str = Field(pattern="^(element_note|recording)$")
    title: str = Field(min_length=1, max_length=200)
    description: str | None = Field(default=None, max_length=10000)
    page_url: str | None = Field(default=None, max_length=2048)
    page_title: str | None = Field(default=None, max_length=512)
    environment: EnvironmentIn | None = None
    element: ElementContextIn | None = None
    mouse_track: MouseTrackIn | None = None
    attachment_ids: list[uuid.UUID] | None = None  # uploaded files to attach
    annotation_shapes: dict[uuid.UUID, list] | None = None  # attachment_id -> vector shapes
    steps: list[StepIn] | None = None


class ReportUpdateIn(BaseModel):
    title: str | None = Field(default=None, min_length=1, max_length=200)
    description: str | None = Field(default=None, max_length=10000)
    status: str | None = Field(default=None, pattern="^(open|fixed|wont_fix)$")


class AuthorOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    nickname: str


class AttachmentOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    file_id: uuid.UUID
    kind: str
    filename: str
    mime: str
    size: int
    annotation_shapes: list | None


class StepOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    step_index: int
    type: str
    offset_ms: int
    data: dict
    screenshot_attachment_id: uuid.UUID | None


class ReportOut(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: uuid.UUID
    type: str
    title: str
    description: str | None
    status: str
    page_url: str | None
    page_title: str | None
    environment: dict
    element: dict | None
    mouse_track: dict | None
    share_token: str
    created_at: datetime
    updated_at: datetime


class ReportListItemOut(ReportOut):
    author: AuthorOut


class ReportDetailOut(ReportOut):
    author: AuthorOut
    attachments: list[AttachmentOut]
    steps: list[StepOut]
    # lets the panel link back to the project page even on a cold share-link
    # visit (no in-app history); set only by the detail GET
    project_share_token: str | None = None


class ReportListOut(BaseModel):
    items: list[ReportListItemOut]
    total: int
    limit: int
    offset: int


# ---------- uploads ----------


class UploadOut(BaseModel):
    file_id: uuid.UUID
    mime: str
    size: int
    filename: str