"""Packages and memberships — Phase 14.5."""
from __future__ import annotations

import uuid
from datetime import date, datetime

from sqlalchemy import Date, DateTime, Index, Integer, Numeric, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON

from app.core.database import Base
from app.models.base import (
    ActorAuditMixin,
    OptimisticLockMixin,
    SoftDeleteMixin,
    TenantMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)
from app.models.types import (
    GUID,
    LifecycleStatus,
    MembershipLifecycleAction,
    MembershipStatus,
    PackageStatus,
)


class PackageDefinition(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "package_definitions"

    organization_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)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    total_sessions: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
    price: Mapped[float | None] = mapped_column(Numeric(18, 2), nullable=True)
    valid_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_package_definitions_tenant_code"),
        Index("ix_package_definitions_org", "tenant_id", "organization_id"),
    )


class PackageLine(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "package_lines"

    package_definition_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    service_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    session_count: Mapped[int] = mapped_column(Integer, default=1, nullable=False)

    __table_args__ = (
        Index("ix_package_lines_package", "tenant_id", "package_definition_id"),
    )


class CustomerPackage(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    __tablename__ = "customer_packages"

    customer_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    package_definition_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    status: Mapped[PackageStatus] = mapped_column(
        default=PackageStatus.ACTIVE, nullable=False
    )
    sessions_remaining: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    purchased_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    expires_at: Mapped[date | None] = mapped_column(Date, nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_customer_packages_tenant_code"),
        Index("ix_customer_packages_customer", "tenant_id", "customer_id"),
    )


class PackageRedemption(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    ActorAuditMixin,
):
    __tablename__ = "package_redemptions"

    customer_package_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    appointment_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    service_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    redeemed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
    sessions_used: Mapped[int] = mapped_column(Integer, default=1, nullable=False)

    __table_args__ = (
        Index("ix_package_redemptions_package", "tenant_id", "customer_package_id"),
    )


class MembershipPlan(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    __tablename__ = "membership_plans"

    organization_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)
    description: Mapped[str | None] = mapped_column(Text, nullable=True)
    billing_interval_days: Mapped[int] = mapped_column(Integer, default=30, nullable=False)
    price: Mapped[float | None] = mapped_column(Numeric(18, 2), nullable=True)
    benefits: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_membership_plans_tenant_code"),
        Index("ix_membership_plans_org", "tenant_id", "organization_id"),
    )


class CustomerMembership(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
    OptimisticLockMixin,
):
    __tablename__ = "customer_memberships"

    customer_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    membership_plan_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    code: Mapped[str] = mapped_column(String(50), nullable=False)
    status: Mapped[MembershipStatus] = mapped_column(
        default=MembershipStatus.PENDING, nullable=False
    )
    started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    expires_at: Mapped[date | None] = mapped_column(Date, nullable=True)
    external_loyalty_ref: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        UniqueConstraint("tenant_id", "code", name="uq_customer_memberships_tenant_code"),
        Index("ix_customer_memberships_customer", "tenant_id", "customer_id"),
    )


class MembershipLifecycleEvent(Base, UUIDPrimaryKeyMixin, TenantMixin, TimestampMixin):
    __tablename__ = "membership_lifecycle_events"

    customer_membership_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
    action: Mapped[MembershipLifecycleAction] = mapped_column(nullable=False)
    from_status: Mapped[MembershipStatus] = mapped_column(nullable=False)
    to_status: Mapped[MembershipStatus] = mapped_column(nullable=False)
    reason: Mapped[str | None] = mapped_column(String(500), nullable=True)
    actor_user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)

    __table_args__ = (
        Index(
            "ix_membership_lifecycle_events_membership",
            "tenant_id",
            "customer_membership_id",
        ),
    )
