"""Membership Catalog aggregates — Phase 9.1.

Catalog definitions only (types, packages, plans, pricing, age groups, categories,
rules and policies). Member enrollment workflows belong to Phase 9.2+.
"""
from __future__ import annotations

import uuid
from datetime import date
from decimal import Decimal

from sqlalchemy import (
    Boolean,
    Date,
    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,
    SoftDeleteMixin,
    TenantMixin,
    TimestampMixin,
    UUIDPrimaryKeyMixin,
)
from app.models.types import BillingPeriod, GUID, LifecycleStatus, PricingModelKind, RuleKind


class SportCategory(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Configurable sport category for catalog grouping (not a hardcoded engine)."""

    __tablename__ = "sport_categories"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    attributes: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_sport_categories_tenant_code",
        ),
        Index("ix_sport_categories_center", "tenant_id", "sports_center_id"),
        Index("ix_sport_categories_tenant_deleted", "tenant_id", "is_deleted"),
    )


class AgeGroup(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Age band used by membership catalog eligibility."""

    __tablename__ = "age_groups"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    min_age: Mapped[int | None] = mapped_column(Integer, nullable=True)
    max_age: Mapped[int | None] = mapped_column(Integer, nullable=True)
    sort_order: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id", "sports_center_id", "code", name="uq_age_groups_tenant_code"
        ),
        Index("ix_age_groups_center", "tenant_id", "sports_center_id"),
        Index("ix_age_groups_tenant_deleted", "tenant_id", "is_deleted"),
    )


class PricingModel(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Catalog pricing model — stores amounts as configuration, not Accounting journals."""

    __tablename__ = "pricing_models"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    kind: Mapped[PricingModelKind] = mapped_column(
        default=PricingModelKind.FIXED, nullable=False
    )
    billing_period: Mapped[BillingPeriod] = mapped_column(
        default=BillingPeriod.MONTHLY, nullable=False
    )
    currency_code: Mapped[str] = mapped_column(String(3), default="IRR", nullable=False)
    amount: Mapped[Decimal | None] = mapped_column(Numeric(18, 2), nullable=True)
    tiers: Mapped[list | None] = mapped_column(JSON, nullable=True)
    tax_inclusive: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    external_price_ref: Mapped[str | None] = mapped_column(String(100), nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_pricing_models_tenant_code",
        ),
        Index("ix_pricing_models_center", "tenant_id", "sports_center_id"),
        Index("ix_pricing_models_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MembershipPackage(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Bundled commercial offering composed of catalog items."""

    __tablename__ = "membership_packages"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    pricing_model_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    sport_ids: Mapped[list | None] = mapped_column(JSON, nullable=True)
    included_items: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    session_credits: Mapped[int | None] = mapped_column(Integer, nullable=True)
    valid_from: Mapped[date | None] = mapped_column(Date, nullable=True)
    valid_until: Mapped[date | None] = mapped_column(Date, nullable=True)
    metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_membership_packages_tenant_code",
        ),
        Index("ix_membership_packages_center", "tenant_id", "sports_center_id"),
        Index("ix_membership_packages_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MembershipPlan(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Membership plan definition (duration / commercial schedule)."""

    __tablename__ = "membership_plans"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    duration_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    billing_period: Mapped[BillingPeriod] = mapped_column(
        default=BillingPeriod.MONTHLY, nullable=False
    )
    pricing_model_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    package_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    age_group_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    sport_category_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    sport_ids: Mapped[list | None] = mapped_column(JSON, nullable=True)
    features: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_membership_plans_tenant_code",
        ),
        Index("ix_membership_plans_center", "tenant_id", "sports_center_id"),
        Index("ix_membership_plans_tenant_deleted", "tenant_id", "is_deleted"),
    )


class MembershipRule(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Declarative membership catalog rule (eligibility/access/usage)."""

    __tablename__ = "membership_rules"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    kind: Mapped[RuleKind] = mapped_column(default=RuleKind.ELIGIBILITY, nullable=False)
    membership_type_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    plan_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    package_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    priority: Mapped[int] = mapped_column(Integer, default=100, nullable=False)
    expression: Mapped[dict | None] = mapped_column(JSON, nullable=True)
    is_blocking: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_membership_rules_tenant_code",
        ),
        Index("ix_membership_rules_center", "tenant_id", "sports_center_id"),
        Index("ix_membership_rules_tenant_deleted", "tenant_id", "is_deleted"),
    )


class RenewalPolicy(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Catalog renewal policy definition."""

    __tablename__ = "renewal_policies"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    auto_renew: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    renew_window_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    grace_period_days: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    max_renewals: Mapped[int | None] = mapped_column(Integer, nullable=True)
    pricing_model_id: Mapped[uuid.UUID | None] = mapped_column(GUID(), nullable=True)
    rules: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_renewal_policies_tenant_code",
        ),
        Index("ix_renewal_policies_center", "tenant_id", "sports_center_id"),
        Index("ix_renewal_policies_tenant_deleted", "tenant_id", "is_deleted"),
    )


class FreezingRule(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Catalog freeze rules for memberships."""

    __tablename__ = "freezing_rules"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    max_freeze_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    max_freeze_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
    min_active_days_before_freeze: Mapped[int | None] = mapped_column(
        Integer, nullable=True
    )
    extend_end_date: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    requires_approval: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    rules: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_freezing_rules_tenant_code",
        ),
        Index("ix_freezing_rules_center", "tenant_id", "sports_center_id"),
        Index("ix_freezing_rules_tenant_deleted", "tenant_id", "is_deleted"),
    )


class ExpirationPolicy(
    Base,
    UUIDPrimaryKeyMixin,
    TenantMixin,
    TimestampMixin,
    SoftDeleteMixin,
    ActorAuditMixin,
):
    """Catalog expiration / lapse policy."""

    __tablename__ = "expiration_policies"

    sports_center_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)
    status: Mapped[LifecycleStatus] = mapped_column(
        default=LifecycleStatus.ACTIVE, nullable=False
    )
    expire_after_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    warn_before_days: Mapped[int | None] = mapped_column(Integer, nullable=True)
    grace_period_days: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
    auto_expire: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    post_expire_status: Mapped[str] = mapped_column(
        String(32), default="expired", nullable=False
    )
    rules: Mapped[dict | None] = mapped_column(JSON, nullable=True)

    __table_args__ = (
        UniqueConstraint(
            "tenant_id",
            "sports_center_id",
            "code",
            name="uq_expiration_policies_tenant_code",
        ),
        Index("ix_expiration_policies_center", "tenant_id", "sports_center_id"),
        Index("ix_expiration_policies_tenant_deleted", "tenant_id", "is_deleted"),
    )
