from __future__ import annotations

from datetime import datetime
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field


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

    id: str
    tenant_id: str | None = None
    user_id: str | None = None
    api_key_id: str | None = None
    action: str
    object_type: str | None = None
    object_id: str | None = None
    details: dict[str, Any] | None = None
    created_at: datetime


class AuditLogListResponse(BaseModel):
    items: list[AuditLogItemResponse]


class DeltaDeletedItem(BaseModel):
    id: str
    resource_type: str | None = None
    revision: str | None = None
    deleted_at: datetime | None = None


class DeltaCollectionResponse(BaseModel):
    items: list[dict[str, Any]] = Field(default_factory=list)
    deleted: list[DeltaDeletedItem] = Field(default_factory=list)
    watermark: str | None = None
    has_more: bool = False
    full: bool = False


class ReferenceOptionResponse(BaseModel):
    value: str
    label: str
    description: str | None = None
    kind: str | None = None
    availability: Literal["available", "inactive", "unavailable"] = "available"
    disabled: bool = False
    source_module: str | None = None
    provenance: dict[str, Any] = Field(default_factory=dict)


class ReferenceOptionListResponse(BaseModel):
    options: list[ReferenceOptionResponse] = Field(default_factory=list)
    provider_available: bool = True
    next_cursor: str | None = None
    has_more: bool = False


class LoginRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    email: str
    password: str
    # Kept optional for backwards compatibility and future tenant-switch login flows.
    # The WebUI no longer sends it. If omitted, the backend resolves the user by email.
    tenant_slug: str | None = None


class SwitchTenantRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    tenant_id: str


class SwitchActingContextRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    assignment_id: str | None = Field(default=None, max_length=36)


class TenantInfo(BaseModel):
    id: str
    slug: str
    name: str
    is_active: bool = True
    default_locale: str = "en"
    enabled_language_codes: list[str] = Field(default_factory=list)


class TenantMembershipInfo(TenantInfo):
    roles: list[str] = Field(default_factory=list)
    is_active: bool = True


class UserUiPreferences(BaseModel):
    model_config = ConfigDict(extra="ignore")

    compact_tables: bool = False
    show_inline_help_hints: bool = True
    reduce_motion: bool = False
    sticky_section_sidebars: bool = True
    theme: Literal["system", "light", "dark"] = "system"


class UserInfo(BaseModel):
    id: str
    account_id: str
    email: str
    # Global account identity used by the title bar and account settings.
    display_name: str | None = None
    # Optional tenant-local alias used in tenant administration and ownership.
    tenant_display_name: str | None = None
    is_tenant_admin: bool = False
    password_reset_required: bool = False
    preferred_language: str | None = None
    enabled_language_codes: list[str] = Field(default_factory=list)
    ui_preferences: UserUiPreferences = Field(default_factory=UserUiPreferences)


class AuthSessionUserInfo(BaseModel):
    id: str
    account_id: str
    email: str
    display_name: str | None = None
    tenant_display_name: str | None = None
    is_tenant_admin: bool = False
    password_reset_required: bool = False


class AuthSessionResponse(BaseModel):
    authenticated: bool = True
    auth_method: Literal["session", "api_key"] = "session"
    user: AuthSessionUserInfo
    # Backwards-compatible alias for the active tenant.
    tenant: TenantInfo
    active_tenant: TenantInfo
    session_id: str | None = None
    api_key_id: str | None = None
    expires_at: datetime | None = None


class ProfileUpdateRequest(BaseModel):
    model_config = ConfigDict(extra="forbid")

    display_name: str | None = Field(default=None, max_length=255)
    tenant_display_name: str | None = Field(default=None, max_length=255)
    preferred_language: str | None = Field(default=None, max_length=20)
    enabled_language_codes: list[str] | None = None
    ui_preferences: UserUiPreferences | None = None


class LanguageInfo(BaseModel):
    code: str
    label: str
    native_label: str | None = None


class RoleInfo(BaseModel):
    id: str
    slug: str
    name: str
    permissions: list[str] = Field(default_factory=list)
    level: Literal["tenant", "system"] = "tenant"


class GroupInfo(BaseModel):
    id: str
    slug: str
    name: str


class PrincipalContextInfo(BaseModel):
    account_id: str
    membership_id: str | None = None
    tenant_id: str | None = None
    identity_id: str | None = None
    scopes: list[str] = Field(default_factory=list)
    group_ids: list[str] = Field(default_factory=list)
    role_ids: list[str] = Field(default_factory=list)
    function_assignment_ids: list[str] = Field(default_factory=list)
    delegation_ids: list[str] = Field(default_factory=list)
    auth_method: Literal["session", "api_key", "service_account"] = "session"
    api_key_id: str | None = None
    session_id: str | None = None
    service_account_id: str | None = None
    acting_assignment_id: str | None = None
    acting_for_account_id: str | None = None
    email: str | None = None
    display_name: str | None = None


class AuthShellResponse(BaseModel):
    user: UserInfo
    # Backwards-compatible alias for the active tenant.
    tenant: TenantInfo
    active_tenant: TenantInfo
    tenants: list[TenantMembershipInfo] = Field(default_factory=list)
    scopes: list[str]
    principal: PrincipalContextInfo | None = None
    profile_loaded: bool = False
    roles_loaded: bool = False
    groups_loaded: bool = False


class AuthProfileResponse(BaseModel):
    user: UserInfo
    # Backwards-compatible alias for the active tenant.
    tenant: TenantInfo
    active_tenant: TenantInfo
    available_languages: list[LanguageInfo] = Field(default_factory=list)
    enabled_language_codes: list[str] = Field(default_factory=list)
    default_language: str = "en"
    profile_loaded: bool = True


class AuthRolesResponse(BaseModel):
    roles: list[RoleInfo] = Field(default_factory=list)
    roles_loaded: bool = True


class AuthGroupsResponse(BaseModel):
    groups: list[GroupInfo] = Field(default_factory=list)
    groups_loaded: bool = True


class LoginResponse(BaseModel):
    access_token: str
    token_type: str = "bearer"  # noqa: S105 - OAuth token type, not a credential.
    expires_at: datetime
    user: UserInfo
    # Backwards-compatible alias for the active tenant.
    tenant: TenantInfo
    active_tenant: TenantInfo
    tenants: list[TenantMembershipInfo] = Field(default_factory=list)
    scopes: list[str]
    roles: list[RoleInfo] = Field(default_factory=list)
    groups: list[GroupInfo] = Field(default_factory=list)
    principal: PrincipalContextInfo | None = None
    available_languages: list[LanguageInfo] = Field(default_factory=list)
    enabled_language_codes: list[str] = Field(default_factory=list)
    default_language: str = "en"
    profile_loaded: bool = True
    roles_loaded: bool = True
    groups_loaded: bool = True


class MeResponse(BaseModel):
    user: UserInfo
    # Backwards-compatible alias for the active tenant.
    tenant: TenantInfo
    active_tenant: TenantInfo
    tenants: list[TenantMembershipInfo] = Field(default_factory=list)
    scopes: list[str]
    roles: list[RoleInfo] = Field(default_factory=list)
    groups: list[GroupInfo] = Field(default_factory=list)
    principal: PrincipalContextInfo | None = None
    available_languages: list[LanguageInfo] = Field(default_factory=list)
    enabled_language_codes: list[str] = Field(default_factory=list)
    default_language: str = "en"
    profile_loaded: bool = True
    roles_loaded: bool = True
    groups_loaded: bool = True
