# app/modules/loyalty/schemas/program.py
"""
Pydantic schemas for loyalty program operations.

Merchant-based programs:
- One program per merchant
- All stores under a merchant share the same program
- Supports chain-wide loyalty across locations
"""

from datetime import datetime

from pydantic import BaseModel, ConfigDict, Field


class PointsRewardConfig(BaseModel):
    """Configuration for a points-based reward."""

    id: str = Field(..., description="Unique reward identifier")
    name: str = Field(..., max_length=100, description="Reward name")
    points_required: int = Field(..., gt=0, description="Points needed to redeem")
    description: str | None = Field(None, max_length=255, description="Reward description")
    is_active: bool = Field(True, description="Whether reward is currently available")


class TierConfig(BaseModel):
    """Configuration for a loyalty tier (future use)."""

    id: str = Field(..., description="Tier identifier")
    name: str = Field(..., max_length=50, description="Tier name (e.g., Bronze, Silver, Gold)")
    points_threshold: int = Field(..., ge=0, description="Points needed to reach this tier")
    benefits: list[str] = Field(default_factory=list, description="List of tier benefits")
    multiplier: float = Field(1.0, ge=1.0, description="Points earning multiplier")


class ProgramCreate(BaseModel):
    """Schema for creating a loyalty program."""

    # Program type
    loyalty_type: str = Field(
        "points",
        pattern="^(stamps|points|hybrid)$",
        description="Program type: stamps, points, or hybrid",
    )

    # Stamps configuration
    stamps_target: int = Field(10, ge=1, le=50, description="Stamps needed for reward")
    stamps_reward_description: str = Field(
        "Free item",
        max_length=255,
        description="Description of stamp reward",
    )
    stamps_reward_value_cents: int | None = Field(
        None,
        ge=0,
        description="Value of reward in cents (for analytics)",
    )

    # Points configuration
    points_per_euro: int = Field(1, ge=1, le=100, description="Points per euro spent")
    points_rewards: list[PointsRewardConfig] = Field(
        default_factory=list,
        description="Available point rewards",
    )
    points_expiration_days: int | None = Field(
        None,
        ge=30,
        description="Days of inactivity before points expire (None = never)",
    )
    welcome_bonus_points: int = Field(
        0,
        ge=0,
        description="Bonus points awarded on enrollment",
    )
    minimum_redemption_points: int = Field(
        100,
        ge=1,
        description="Minimum points required for redemption",
    )
    minimum_purchase_cents: int = Field(
        0,
        ge=0,
        description="Minimum purchase amount to earn points (0 = no minimum)",
    )

    # Future: Tier configuration
    tier_config: list[TierConfig] | None = Field(
        None,
        description="Tier configuration (future use)",
    )

    # Anti-fraud
    cooldown_minutes: int = Field(15, ge=0, le=1440, description="Minutes between stamps")
    max_daily_stamps: int = Field(5, ge=1, le=50, description="Max stamps per card per day")
    require_staff_pin: bool = Field(True, description="Require staff PIN for operations")

    # Branding
    card_name: str | None = Field(None, max_length=100, description="Display name for card")
    card_color: str = Field(
        "#4F46E5",
        pattern="^#[0-9A-Fa-f]{6}$",
        description="Primary color (hex)",
    )
    card_secondary_color: str | None = Field(
        None,
        pattern="^#[0-9A-Fa-f]{6}$",
        description="Secondary color (hex)",
    )
    logo_url: str | None = Field(None, max_length=500, description="Logo URL")
    hero_image_url: str | None = Field(None, max_length=500, description="Hero image URL")

    # Terms
    terms_text: str | None = Field(None, description="Terms and conditions (legacy)")
    terms_cms_page_slug: str | None = Field(None, max_length=200, description="CMS page slug for T&C")
    privacy_url: str | None = Field(None, max_length=500, description="Privacy policy URL")


class ProgramUpdate(BaseModel):
    """Schema for updating a loyalty program."""

    model_config = ConfigDict(from_attributes=True)

    # Program type (cannot change from stamps to points after cards exist)
    loyalty_type: str | None = Field(
        None,
        pattern="^(stamps|points|hybrid)$",
    )

    # Stamps configuration
    stamps_target: int | None = Field(None, ge=1, le=50)
    stamps_reward_description: str | None = Field(None, max_length=255)
    stamps_reward_value_cents: int | None = Field(None, ge=0)

    # Points configuration
    points_per_euro: int | None = Field(None, ge=1, le=100)
    points_rewards: list[PointsRewardConfig] | None = None
    points_expiration_days: int | None = Field(None, ge=30)
    welcome_bonus_points: int | None = Field(None, ge=0)
    minimum_redemption_points: int | None = Field(None, ge=1)
    minimum_purchase_cents: int | None = Field(None, ge=0)

    # Future: Tier configuration
    tier_config: list[TierConfig] | None = None

    # Anti-fraud
    cooldown_minutes: int | None = Field(None, ge=0, le=1440)
    max_daily_stamps: int | None = Field(None, ge=1, le=50)
    require_staff_pin: bool | None = None

    # Branding
    card_name: str | None = Field(None, max_length=100)
    card_color: str | None = Field(None, pattern="^#[0-9A-Fa-f]{6}$")
    card_secondary_color: str | None = Field(None, pattern="^#[0-9A-Fa-f]{6}$")
    logo_url: str | None = Field(None, max_length=500)
    hero_image_url: str | None = Field(None, max_length=500)

    # Terms
    terms_text: str | None = None
    terms_cms_page_slug: str | None = Field(None, max_length=200)
    privacy_url: str | None = Field(None, max_length=500)

    # Wallet integration
    google_issuer_id: str | None = Field(None, max_length=100)
    apple_pass_type_id: str | None = Field(None, max_length=100)

    # Status
    is_active: bool | None = None


class ProgramResponse(BaseModel):
    """Schema for loyalty program response."""

    model_config = ConfigDict(from_attributes=True)

    id: int
    merchant_id: int
    merchant_name: str | None = None  # Populated by API from Merchant join
    loyalty_type: str

    # Stamps
    stamps_target: int
    stamps_reward_description: str
    stamps_reward_value_cents: int | None = None

    # Points
    points_per_euro: int
    points_rewards: list[PointsRewardConfig] = []
    points_expiration_days: int | None = None
    welcome_bonus_points: int = 0
    minimum_redemption_points: int = 100
    minimum_purchase_cents: int = 0

    # Anti-fraud
    cooldown_minutes: int
    max_daily_stamps: int
    require_staff_pin: bool

    # Branding
    card_name: str | None = None
    card_color: str
    card_secondary_color: str | None = None
    logo_url: str | None = None
    hero_image_url: str | None = None

    # Terms
    terms_text: str | None = None
    terms_cms_page_slug: str | None = None
    privacy_url: str | None = None

    # Wallet
    google_issuer_id: str | None = None
    google_class_id: str | None = None
    apple_pass_type_id: str | None = None

    # Status
    is_active: bool
    activated_at: datetime | None = None
    created_at: datetime
    updated_at: datetime

    # Computed
    is_stamps_enabled: bool = False
    is_points_enabled: bool = False
    display_name: str = "Loyalty Card"

    # Stats (populated by API)
    total_cards: int | None = None
    active_cards: int | None = None
    total_points_issued: int | None = None
    total_points_redeemed: int | None = None


class StorefrontProgramResponse(ProgramResponse):
    """Program response for unauthenticated storefront visitors — excludes wallet integration IDs."""

    model_config = ConfigDict(from_attributes=True)

    google_issuer_id: str | None = Field(None, exclude=True)
    google_class_id: str | None = Field(None, exclude=True)
    apple_pass_type_id: str | None = Field(None, exclude=True)


class ProgramListResponse(BaseModel):
    """Schema for listing loyalty programs (admin)."""

    programs: list[ProgramResponse]
    total: int


class ProgramStatsResponse(BaseModel):
    """Schema for program statistics."""

    # Cards
    total_cards: int = 0
    active_cards: int = 0
    new_this_month: int = 0

    # Stamps (if enabled)
    total_stamps_issued: int = 0
    total_stamps_redeemed: int = 0
    stamps_this_month: int = 0
    redemptions_this_month: int = 0

    # Points (if enabled)
    total_points_issued: int = 0
    total_points_redeemed: int = 0
    total_points_balance: int = 0
    points_this_month: int = 0
    points_redeemed_this_month: int = 0

    # Engagement
    cards_with_activity_30d: int = 0
    average_stamps_per_card: float = 0.0
    average_points_per_card: float = 0.0
    avg_points_per_member: float = 0.0

    # 30-day metrics
    transactions_30d: int = 0
    points_issued_30d: int = 0
    points_redeemed_30d: int = 0

    # Value
    estimated_liability_cents: int = 0  # Unredeemed stamps/points value


class MerchantStatsResponse(BaseModel):
    """Schema for merchant-wide loyalty statistics across all locations."""

    merchant_id: int
    program_id: int | None = None  # May be None if no program set up

    # Cards
    total_cards: int = 0
    active_cards: int = 0

    # Points - all time
    total_points_issued: int = 0
    total_points_redeemed: int = 0

    # Members
    new_this_month: int = 0

    # Points - last 30 days
    points_issued_30d: int = 0
    points_redeemed_30d: int = 0
    transactions_30d: int = 0

    # Value
    estimated_liability_cents: int = 0

    # Program info (optional)
    program: dict | None = None

    # Location breakdown
    locations: list[dict] = []  # Per-location breakdown


class MerchantSettingsResponse(BaseModel):
    """Schema for merchant loyalty settings."""

    model_config = ConfigDict(from_attributes=True)

    id: int
    merchant_id: int
    staff_pin_policy: str
    staff_pin_lockout_attempts: int
    staff_pin_lockout_minutes: int
    allow_self_enrollment: bool
    allow_void_transactions: bool
    allow_cross_location_redemption: bool
    created_at: datetime
    updated_at: datetime


class MerchantSettingsUpdate(BaseModel):
    """Schema for updating merchant loyalty settings."""

    staff_pin_policy: str | None = Field(
        None,
        pattern="^(required|optional|disabled)$",
        description="Staff PIN policy: required, optional, or disabled",
    )
    staff_pin_lockout_attempts: int | None = Field(None, ge=3, le=10)
    staff_pin_lockout_minutes: int | None = Field(None, ge=5, le=120)
    allow_self_enrollment: bool | None = None
    allow_void_transactions: bool | None = None
    allow_cross_location_redemption: bool | None = None
