"""
Referral and commission models.

Tables in this file:
  referral         — doctors, hospitals, clinics, and agents referring patients
  commission_entry — commission calculation and settlement ledger per order

Referral Architecture & Workflow
─────────────────────────────────
1. Referral Sources:
   Patients may be referred by an external medical doctor, clinic, hospital,
   or independent agent.
   `referral_type`: DOCTOR, HOSPITAL, CLINIC, AGENT, or OTHER.

2. Commission Calculation:
   Each Referral has a default `commission_rate` (percentage, e.g. 10.00%).
   When a VisitOrder is placed with `referral_id`:
     - A CommissionEntry is generated.
     - `order_amount` captures the commissionable base amount at order time.
     - `commission_rate` is captured (snapshot at creation).
     - `commission_amount` = order_amount * commission_rate / 100.

3. Commission Settlement:
   Commissions accrue in the system. Periodic payouts to referral partners
   mark `is_settled = True` and set `settled_at`.

Soft Delete Decisions
─────────────────────
  Referral         -> YES (SoftDeleteMixin): inactive partners are soft-deleted
                      without compromising historical commission calculations.
  CommissionEntry  -> NO  (no soft delete): accounting ledger records are immutable.

Lab Scoping
───────────
  Referral         -> YES (LabScopedMixin): partners belong to a specific branch lab.
  CommissionEntry  -> YES (LabScopedMixin): commissions are accounted at the branch level.
"""

import datetime
import enum
import uuid
from decimal import Decimal

import sqlalchemy as sa
from sqlalchemy import (
    Boolean,
    ForeignKey,
    Numeric,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import TIMESTAMP, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.models.base import (
    Base,
    LabScopedMixin,
    SoftDeleteMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)

# ── Enums ──────────────────────────────────────────────────────────────────────


class ReferralType(enum.StrEnum):
    """Categorization of referral entities."""

    DOCTOR = "doctor"
    HOSPITAL = "hospital"
    CLINIC = "clinic"
    AGENT = "agent"
    OTHER = "other"


ReferralTypeType = sa.Enum(
    ReferralType,
    name="referral_type",
    native_enum=True,
    create_constraint=True,
    values_callable=lambda obj: [e.value for e in obj],
)


# ── Models ─────────────────────────────────────────────────────────────────────


class Referral(UUIDPrimaryKeyMixin, TimestampMixin, SoftDeleteMixin, LabScopedMixin, Base):
    """
    Partner entity (doctor, hospital, or agent) who refers patients to the lab.
    """

    __tablename__ = "referral"
    __table_args__ = (UniqueConstraint("lab_id", "code", name="uq_referral_lab_id_code"),)

    name: Mapped[str] = mapped_column(
        String(200),
        nullable=False,
        index=True,
        comment="Full name of referring doctor, hospital, clinic, or agent",
    )
    referral_type: Mapped[ReferralType] = mapped_column(
        ReferralTypeType,
        nullable=False,
        default=ReferralType.DOCTOR,
        server_default="doctor",
        index=True,
        comment="Type of referral source",
    )
    code: Mapped[str | None] = mapped_column(
        String(30),
        nullable=True,
        index=True,
        comment="Partner identifier code (e.g. 'DOC-001', 'HOSP-KTM')",
    )
    phone: Mapped[str | None] = mapped_column(
        String(20),
        nullable=True,
        comment="Contact phone number",
    )
    email: Mapped[str | None] = mapped_column(
        String(255),
        nullable=True,
        comment="Contact email address",
    )
    address: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Physical clinic or office address",
    )
    commission_rate: Mapped[Decimal] = mapped_column(
        Numeric(5, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        comment="Default commission percentage rate (e.g. 10.00 for 10%)",
    )
    is_active: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=True,
        server_default=sa.true(),
        index=True,
        comment="Active status flag for booking and order referral selection",
    )
    notes: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Internal notes or partnership agreements",
    )

    # Relationships
    lab: Mapped["Lab"] = relationship(  # type: ignore[name-defined]
        "Lab",
        foreign_keys="[Referral.lab_id]",
        lazy="selectin",
    )
    orders: Mapped[list["VisitOrder"]] = relationship(  # type: ignore[name-defined]
        "VisitOrder",
        foreign_keys="[VisitOrder.referral_id]",
        lazy="noload",
    )
    commission_entries: Mapped[list["CommissionEntry"]] = relationship(
        "CommissionEntry",
        back_populates="referral",
        lazy="noload",
    )

    def __repr__(self) -> str:
        return (
            f"<Referral id={self.id!s:.8} "
            f"name={self.name!r} "
            f"type={self.referral_type.value!r} "
            f"rate={self.commission_rate}%>"
        )


class CommissionEntry(UUIDPrimaryKeyMixin, TimestampMixin, LabScopedMixin, Base):
    """
    Financial ledger entry for referral commissions earned on an order.

    Immutable accounting record (no soft delete). Captures snapshot amounts
    at order settlement time and tracks payout status.
    """

    __tablename__ = "commission_entry"
    __table_args__ = (
        UniqueConstraint("order_id", "referral_id", name="uq_commission_entry_order_id_referral_id"),
    )

    # ── Foreign Keys ───────────────────────────────────────────────────────────
    referral_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("referral.id", ondelete="RESTRICT"),
        nullable=False,
        index=True,
        comment="FK → referral.id. The partner receiving this commission.",
    )
    order_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("visit_order.id", ondelete="RESTRICT"),
        nullable=False,
        index=True,
        comment="FK → visit_order.id. The visit order generating this commission.",
    )
    created_by: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("user.id", ondelete="SET NULL"),
        nullable=True,
        index=True,
        comment="FK → user.id. Staff member who calculated or approved this commission.",
    )

    # ── Amounts & Calculation ──────────────────────────────────────────────────
    order_amount: Mapped[Decimal] = mapped_column(
        Numeric(12, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        comment="Base order amount eligible for commission calculation",
    )
    commission_rate: Mapped[Decimal] = mapped_column(
        Numeric(5, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        comment="Commission percentage rate applied (snapshot from referral or override)",
    )
    commission_amount: Mapped[Decimal] = mapped_column(
        Numeric(12, 2),
        nullable=False,
        comment="Calculated commission payout amount",
    )

    # ── Settlement Status ──────────────────────────────────────────────────────
    is_settled: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        server_default=sa.false(),
        index=True,
        comment="Whether this commission has been disbursed/settled to partner",
    )
    settled_at: Mapped[datetime.datetime | None] = mapped_column(
        TIMESTAMP(timezone=True),
        nullable=True,
        comment="Timestamp when commission payout was disbursed (TIMESTAMPTZ)",
    )
    notes: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Accounting settlement notes or payout transaction reference",
    )

    # ── Relationships ──────────────────────────────────────────────────────────
    lab: Mapped["Lab"] = relationship(  # type: ignore[name-defined]
        "Lab",
        foreign_keys="[CommissionEntry.lab_id]",
        lazy="selectin",
    )
    referral: Mapped["Referral"] = relationship(
        "Referral",
        back_populates="commission_entries",
        foreign_keys="[CommissionEntry.referral_id]",
        lazy="selectin",
    )
    order: Mapped["VisitOrder"] = relationship(  # type: ignore[name-defined]
        "VisitOrder",
        foreign_keys="[CommissionEntry.order_id]",
        lazy="selectin",
    )
    created_by_user: Mapped["User | None"] = relationship(  # type: ignore[name-defined]
        "User",
        foreign_keys="[CommissionEntry.created_by]",
        lazy="selectin",
    )

    def __repr__(self) -> str:
        return (
            f"<CommissionEntry id={self.id!s:.8} "
            f"referral_id={self.referral_id!s:.8} "
            f"amount={self.commission_amount} "
            f"settled={self.is_settled}>"
        )


class RadiologistCommission(UUIDPrimaryKeyMixin, TimestampMixin, LabScopedMixin, Base):
    """
    Per-report commission ledger for staff radiologists.

    Accrued once when a report verified by a radiologist is released:
      - `commission_rate` snapshots the radiologist's `User.commission_rate`.
      - `commission_amount` = order_amount * rate / 100.
    Immutable accounting record (no soft delete), settled via payout like referrals.
    """

    __tablename__ = "radiologist_commission"
    __table_args__ = (UniqueConstraint("report_id", name="uq_radiologist_commission_report_id"),)

    radiologist_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("user.id", ondelete="RESTRICT"),
        nullable=False,
        index=True,
        comment="FK → user.id. The radiologist who verified the imaging report.",
    )
    order_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("visit_order.id", ondelete="RESTRICT"),
        nullable=False,
        index=True,
        comment="FK → visit_order.id. The billed visit generating this commission.",
    )
    report_id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("report.id", ondelete="RESTRICT"),
        nullable=False,
        index=True,
        comment="FK → report.id. One commission entry per released report.",
    )
    order_amount: Mapped[Decimal] = mapped_column(
        Numeric(12, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        comment="Base bill/order amount eligible for commission calculation",
    )
    commission_rate: Mapped[Decimal] = mapped_column(
        Numeric(5, 2),
        nullable=False,
        default=Decimal("0.00"),
        server_default="0.00",
        comment="Commission percentage rate snapshot from the radiologist profile",
    )
    commission_amount: Mapped[Decimal] = mapped_column(
        Numeric(12, 2),
        nullable=False,
        comment="Calculated commission payout amount",
    )
    is_settled: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=False,
        server_default=sa.false(),
        index=True,
        comment="Whether this commission has been disbursed to the radiologist",
    )
    settled_at: Mapped[datetime.datetime | None] = mapped_column(
        TIMESTAMP(timezone=True),
        nullable=True,
        comment="Timestamp when commission payout was disbursed",
    )
    notes: Mapped[str | None] = mapped_column(
        Text,
        nullable=True,
        comment="Settlement notes or payout transaction reference",
    )

    # ── Relationships ──────────────────────────────────────────────────────────
    radiologist: Mapped["User"] = relationship(  # type: ignore[name-defined]
        "User",
        foreign_keys="[RadiologistCommission.radiologist_id]",
        lazy="selectin",
    )
    order: Mapped["VisitOrder"] = relationship(  # type: ignore[name-defined]
        "VisitOrder",
        foreign_keys="[RadiologistCommission.order_id]",
        lazy="selectin",
    )

    def __repr__(self) -> str:
        return (
            f"<RadiologistCommission id={self.id!s:.8} "
            f"radiologist_id={self.radiologist_id!s:.8} "
            f"amount={self.commission_amount} "
            f"settled={self.is_settled}>"
        )
