import uuid
import enum
from datetime import datetime, date
from typing import TYPE_CHECKING

from sqlalchemy import Boolean, Date, DateTime, Enum, ForeignKey, String, Text, func
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.core.database import Base

if TYPE_CHECKING:
    from app.models.company import Company
    from app.models.department import Department
    from app.models.session import Session


class UserRole(str, enum.Enum):
    SUPER_ADMIN = "SUPER_ADMIN"
    RESELLER = "RESELLER"
    COMPANY_ADMIN = "COMPANY_ADMIN"
    HR = "HR"
    MANAGER = "MANAGER"
    EMPLOYEE = "EMPLOYEE"


class AuthProvider(str, enum.Enum):
    LOCAL = "local"
    LDAP = "ldap"


class User(Base):
    __tablename__ = "users"

    id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    company_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("companies.id", ondelete="CASCADE"))
    department_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("departments.id", ondelete="SET NULL"))

    email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
    password_hash: Mapped[str | None] = mapped_column(Text, nullable=True)
    first_name: Mapped[str] = mapped_column(String(100), nullable=False)
    last_name: Mapped[str] = mapped_column(String(100), nullable=False)
    role: Mapped[UserRole] = mapped_column(Enum(UserRole), nullable=False, default=UserRole.EMPLOYEE)
    auth_provider: Mapped[AuthProvider] = mapped_column(
        Enum(AuthProvider, name="authprovider", values_callable=lambda x: [e.value for e in x]),
        nullable=False, default=AuthProvider.LOCAL,
    )
    ldap_dn: Mapped[str | None] = mapped_column(Text)
    work_schedule_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True), ForeignKey("work_schedules.id", ondelete="SET NULL")
    )

    # Ein-/Austrittsdatum – Basis für anteilige Urlaubsberechnung (Zwölftel-Regel)
    entry_date: Mapped[date | None] = mapped_column(Date)
    exit_date: Mapped[date | None] = mapped_column(Date)

    # Pro-User-Benachrichtigungseinstellungen (agent-11 PR3). Leeres dict = alle an.
    notification_prefs: Mapped[dict] = mapped_column(JSONB, nullable=False, default=dict, server_default="{}")

    # DSGVO Art. 17: Zeitpunkt der Anonymisierung (Personenbezug entfernt). NULL = aktiv.
    anonymized_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))

    # Kiosk auth
    kiosk_pin_hash: Mapped[str | None] = mapped_column(Text)
    kiosk_qr_token: Mapped[str | None] = mapped_column(Text, unique=True)

    # Kalender-Kürzel (vom Manager setzbar, für CalDAV-Template $kuerzel)
    kuerzel: Mapped[str | None] = mapped_column(String(20))

    # iCal-Abo: gehashtes rotierbares Token für den read-only Kalender-Feed
    # (/absences/ical/<token>.ics). Nur eigene Abwesenheiten, kein JWT nötig.
    ical_token_hash: Mapped[str | None] = mapped_column(Text, unique=True)

    # Personalnummer (numerisch, eindeutig pro Firma; bleibt nach Deaktivierung reserviert)
    personnel_number: Mapped[str | None] = mapped_column(String(50))

    # NFC-UID für Kiosk-Login (optional, eindeutig pro Firma)
    kiosk_nfc_uid: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)

    # TOTP / 2FA
    totp_secret: Mapped[str | None] = mapped_column(String(500))  # Fernet-verschlüsselt
    totp_enabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    # Permissions
    can_manual_time_entry: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    # Account state
    is_active: Mapped[bool] = mapped_column(Boolean, default=True)
    invite_token_hash: Mapped[str | None] = mapped_column(Text)
    invite_expires: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    last_login: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

    # Relationships
    company: Mapped["Company"] = relationship(
        "Company", back_populates="users", foreign_keys="User.company_id",
    )
    department: Mapped["Department | None"] = relationship(
        "Department",
        primaryjoin="User.department_id == Department.id",
        foreign_keys="[User.department_id]",
        back_populates="members",
    )
    sessions: Mapped[list["Session"]] = relationship("Session", back_populates="user", cascade="all, delete-orphan", lazy="noload")

    @property
    def full_name(self) -> str:
        return f"{self.first_name} {self.last_name}"

    def is_admin_or_above(self) -> bool:
        return self.role in (UserRole.COMPANY_ADMIN, UserRole.SUPER_ADMIN)

    def __repr__(self) -> str:
        return f"<User {self.email} [{self.role}]>"
