"""Pydantic-схемы team-management API (#2554, эпик #2549).

CRUD сотрудников (`tradein_users.role = 'employee'`), квоты, история оценок.
Org-изоляция (manager видит/меняет только своих employee) реализована в
`app.api.v1.team`, эти схемы — только форма запросов/ответов.
"""

from __future__ import annotations

import re
from datetime import datetime

from pydantic import BaseModel, ConfigDict, Field, field_validator

# ASCII-only — не-ASCII username ломает downstream identity-пропагацию
# (`app.core.rbac._propagate_authenticated_user` кодирует latin-1 с
# errors="replace"), поэтому валидация формы обязательна на границе API,
# а не только на уровне БД.
#
# `\Z`, НЕ `$` — deep-review seed #2564: в Python `$` матчит перед trailing
# newline (`re.match(r'...\$', 'admin\n')` → True), а Postgres `~` в CHECK
# tradein_users_username_ascii_ck (миграция 193) — False. С `$` строка
# "admin\n" проходила бы Pydantic-валидацию и падала уже в БД → 500 вместо
# честного 422. `\Z` — конец строки БЕЗ поблажки на trailing newline, совпадает
# с семантикой Postgres `~`.
_USERNAME_RE = re.compile(r"^[A-Za-z0-9._-]{3,64}\Z")


class QuotaStatusOut(BaseModel):
    """Статус месячной квоты оценок — вложен в `EmployeeOut`."""

    model_config = ConfigDict(from_attributes=True)

    limit: int
    used: int
    remaining: int
    unlimited: bool


class EmployeeCreateRequest(BaseModel):
    """`POST /employees` — создать сотрудника. Роль всегда `employee` (не в теле)."""

    username: str
    password: str
    display_name: str | None = None
    org_name: str | None = None
    email: str | None = None
    monthly_limit: int | None = Field(default=None, ge=1)
    # Только для actor.role == admin — опциональная привязка к конкретному manager.
    # Для actor.role == manager это поле ИГНОРИРУЕТСЯ (принудительно свой id) —
    # см. app.api.v1.team.create_employee.
    manager_id: int | None = None

    @field_validator("username")
    @classmethod
    def _validate_username(cls, v: str) -> str:
        if not _USERNAME_RE.match(v):
            raise ValueError(
                "username must be 3-64 ASCII chars: letters, digits, dot, underscore, hyphen"
            )
        return v


class EmployeeUpdateRequest(BaseModel):
    """`PATCH /employees/{id}` — частичное обновление, все поля опциональны."""

    is_active: bool | None = None
    monthly_limit: int | None = Field(default=None, ge=1)
    display_name: str | None = None
    org_name: str | None = None
    email: str | None = None
    new_password: str | None = None


class EmployeeOut(BaseModel):
    """Одна строка в `GET /employees` + ответ `POST`/`PATCH /employees/{id}`."""

    model_config = ConfigDict(from_attributes=True)

    id: int
    username: str
    display_name: str | None = None
    org_name: str | None = None
    email: str | None = None
    is_active: bool
    manager_id: int | None = None
    created_at: datetime
    quota: QuotaStatusOut


class EmployeeHistoryEntry(BaseModel):
    """Одна строка истории оценок сотрудника — `GET /employees/{id}/history`.

    Источник — `user_events` (event_type='estimate_request', паттерн
    `app.api.v1.audit.account_drilldown`), LEFT JOIN на `trade_in_estimates`
    за фактическим результатом (median_price/confidence/n_analogs) — join
    может не сматчиться (старая запись без estimate_id / оценка insufficient_data),
    поэтому все result-поля nullable.
    """

    model_config = ConfigDict(from_attributes=True)

    estimate_id: str | None = None
    address: str | None = None
    area_m2: str | None = None
    rooms: str | None = None
    median_price: int | None = None
    confidence: str | None = None
    n_analogs: int | None = None
    created_at: datetime
