"""Referral Engine aggregates — Phase 7.5.

Independent aggregates (no SQLAlchemy relationships), same as other Loyalty
aggregates. Bonus points are always the ledger's responsibility; these rows
only record configuration, code ownership, and referrer→referee tracking.
"""
from __future__ import annotations

import uuid
from datetime import datetime

from sqlalchemy import DateTime, Index, Integer, String, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column

from app.core.database import Base
from app.models.base import (
    ActorAuditMixin,
    SoftDeleteMixin,
    TenantMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)
from app.models.types import (
    GUID,
    ReferralAttributionStatus,
    ReferralCodeStatus,
    ReferralProgramStatus,
)


class ReferralProgram(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Referral program configuration — bonus rules for referrer/referee."""

    __tablename__ = "referral_programs"

    program_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    status: Mapped[ReferralProgramStatus] = mapped_column(
        default=ReferralProgramStatus.DRAFT, nullable=False
    )
    referrer_bonus_points: Mapped[int] = mapped_column(
        Integer, default=0, nullable=False
    )
    referee_bonus_points: Mapped[int] = mapped_column(
        Integer, default=0, nullable=False
    )
    max_referrals_per_member: Mapped[int | None] = mapped_column(
        Integer, nullable=True
    )

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "program_id",
            "code",
            name="uq_referral_programs_tenant_program_code",
        ),
        Index("ix_referral_programs_tenant_status", "tenant_id", "status"),
        Index("ix_referral_programs_tenant_deleted", "tenant_id", "is_deleted"),
    )


class ReferralCode(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
):
    """Referral code owned by a member (the referrer). Status-only lifecycle."""

    __tablename__ = "referral_codes"

    referral_program_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    program_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    status: Mapped[ReferralCodeStatus] = mapped_column(
        default=ReferralCodeStatus.ACTIVE, nullable=False
    )
    uses_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    created_by: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_referral_codes_tenant_code"),
        Index(
            "ix_referral_codes_program_member",
            "tenant_id",
            "referral_program_id",
            "member_id",
        ),
    )


class ReferralAttribution(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
):
    """Referrer -> referee link and its lifecycle through reward."""

    __tablename__ = "referral_attributions"

    referral_program_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    referral_code_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    program_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    referrer_member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    referee_member_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    status: Mapped[ReferralAttributionStatus] = mapped_column(
        default=ReferralAttributionStatus.PENDING, nullable=False
    )
    referrer_ledger_entry_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID(), nullable=True
    )
    referee_ledger_entry_id: Mapped[uuid.UUID | None] = mapped_column(
        GUID(), nullable=True
    )
    idempotency_key: Mapped[str | None] = mapped_column(String(100), nullable=True)
    converted_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    rewarded_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    cancelled_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    cancel_reason: Mapped[str | None] = mapped_column(String(500), nullable=True)
    actor_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "referral_program_id",
            "referee_member_id",
            name="uq_referral_attributions_tenant_program_referee",
        ),
        UniqueConstraint(
            "tenant_id",
            "idempotency_key",
            name="uq_referral_attributions_tenant_idempotency",
        ),
        Index(
            "ix_referral_attributions_referrer",
            "tenant_id",
            "referral_program_id",
            "referrer_member_id",
        ),
        Index("ix_referral_attributions_status", "tenant_id", "status"),
    )
