import uuid
from datetime import datetime
from typing import Literal

from pydantic import BaseModel, ConfigDict, model_validator

from app.schemas.reporting import DatasetFilter, DatasetFilterItem

RecordAccessScope = Literal["all", "own", "filtered"]


class RecordAccessRule(BaseModel):
    """What the holders of `role_ids` (empty = everyone) may see of one
    Master's records or Form's submissions — every record ("all"), the ones
    they created ("own"), or the ones matching `filters` ("filtered"; values
    may use {{current_user.id}} / {{session.<key>}} tokens, resolved per
    viewer) — and whether they may edit / delete the records they see.
    A person matching several rules gets their union."""

    role_ids: list[uuid.UUID] = []
    scope: RecordAccessScope = "all"
    filters: list[DatasetFilterItem] = []
    can_edit: bool = True
    can_delete: bool = True

    @model_validator(mode="after")
    def _filters_need_filtered_scope(self) -> "RecordAccessRule":
        if self.scope == "filtered" and not self.filters:
            raise ValueError("a 'filtered' rule needs at least one filter")
        if self.scope != "filtered":
            self.filters = []
        return self


class RecordAccessPolicyIn(BaseModel):
    """enabled=False (or no rules) leaves every record visible to anyone with
    the module permission. Enabled with no rule matching a person: they see
    nothing. System administrators are never restricted."""

    enabled: bool = False
    rules: list[RecordAccessRule] = []
    # The policy's updated_at as last read — a save over someone else's newer
    # change is refused (409) instead of silently overwriting it.
    expected_updated_at: datetime | None = None


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

    source_type: str
    source_id: uuid.UUID
    enabled: bool
    rules: list[RecordAccessRule]
    updated_at: datetime | None = None
    updated_by: uuid.UUID | None = None
