# app/modules/loyalty/routes/api/storefront.py
"""
Loyalty Module - Storefront API Routes

Customer-facing endpoints for:
- View loyalty card and balance
- View transaction history
- Self-service enrollment
- Get program information

Uses store from middleware context (StoreContextMiddleware).
"""

import logging

from fastapi import APIRouter, Depends, Query, Request
from sqlalchemy.orm import Session

from app.api.deps import get_current_customer_api
from app.core.database import get_db
from app.modules.customers.schemas import CustomerContext
from app.modules.loyalty.exceptions import LoyaltyCardAlreadyExistsException
from app.modules.loyalty.schemas import (
    CardEnrollRequest,
    CardResponse,
    ProgramResponse,
)
from app.modules.loyalty.schemas.program import StorefrontProgramResponse
from app.modules.loyalty.services import card_service, program_service, wallet_service
from app.modules.tenancy.exceptions import StoreNotFoundException
from middleware.decorators import rate_limit

storefront_router = APIRouter()
logger = logging.getLogger(__name__)


# =============================================================================
# Public Endpoints (No Authentication Required)
# =============================================================================


@storefront_router.get("/loyalty/program")
@rate_limit(max_requests=30, window_seconds=60)
def get_program_info(
    request: Request,
    db: Session = Depends(get_db),
):
    """
    Get loyalty program information for current store.
    Public endpoint - no authentication required.
    """
    store = getattr(request.state, "store", None)
    if not store:
        raise StoreNotFoundException("context", identifier_type="subdomain")

    program = program_service.get_program_by_store(db, store.id)
    if not program:
        return None

    response = StorefrontProgramResponse.model_validate(program)
    response.is_stamps_enabled = program.is_stamps_enabled
    response.is_points_enabled = program.is_points_enabled
    response.display_name = program.display_name

    return response


@storefront_router.post("/loyalty/enroll")
@rate_limit(max_requests=10, window_seconds=60)
def self_enroll(
    request: Request,
    data: CardEnrollRequest,
    db: Session = Depends(get_db),
):
    """
    Self-service enrollment.
    Public endpoint - customers can enroll via QR code without authentication.
    """
    store = getattr(request.state, "store", None)
    if not store:
        raise StoreNotFoundException("context", identifier_type="subdomain")

    # Check if self-enrollment is allowed
    program_service.check_self_enrollment_allowed(db, store.merchant_id)

    # Resolve customer_id (create customer if not found for self-enrollment)
    customer_id = card_service.resolve_customer_id(
        db,
        customer_id=data.customer_id,
        email=data.email,
        store_id=store.id,
        merchant_id=store.merchant_id,
        create_if_missing=True,
        customer_name=data.customer_name,
        customer_phone=data.customer_phone,
        customer_birthday=data.customer_birthday,
    )

    logger.info(f"Self-enrollment for customer {customer_id} at store {store.subdomain}")

    # Build merchant context for the response (locations, cross-location flag)
    settings = program_service.get_merchant_settings(db, store.merchant_id)
    allow_cross_location = (
        settings.allow_cross_location_redemption if settings else True
    )
    locations = program_service.get_merchant_locations(db, store.merchant_id)
    location_list = [
        {"id": loc.id, "name": loc.name}
        for loc in locations
    ]

    already_enrolled = False
    try:
        card = card_service.enroll_customer_for_store(db, customer_id, store.id)
    except LoyaltyCardAlreadyExistsException:
        # Customer already has a card — return it instead of erroring out.
        # For cross-location=true this is the normal re-enroll-at-another-store
        # path; for cross-location=false this is a same-store re-enroll.
        already_enrolled = True
        if allow_cross_location:
            card = card_service.get_card_by_customer_and_merchant(
                db, customer_id, store.merchant_id
            )
        else:
            card = card_service.get_card_by_customer_and_store(
                db, customer_id, store.id
            )

    program = card.program
    wallet_urls = wallet_service.get_add_to_wallet_urls(db, card)

    # Resolve the name of the original enrollment store
    enrolled_at_store_name = None
    if card.enrolled_at_store_id:
        for loc in locations:
            if loc.id == card.enrolled_at_store_id:
                enrolled_at_store_name = loc.name
                break

    return {
        "card": CardResponse(
            id=card.id,
            card_number=card.card_number,
            customer_id=card.customer_id,
            merchant_id=card.merchant_id,
            program_id=card.program_id,
            enrolled_at_store_id=card.enrolled_at_store_id,
            stamp_count=card.stamp_count,
            stamps_target=program.stamps_target,
            stamps_until_reward=max(0, program.stamps_target - card.stamp_count),
            total_stamps_earned=card.total_stamps_earned,
            stamps_redeemed=card.stamps_redeemed,
            points_balance=card.points_balance,
            total_points_earned=card.total_points_earned,
            points_redeemed=card.points_redeemed,
            is_active=card.is_active,
            created_at=card.created_at,
            has_google_wallet=bool(card.google_object_id),
            has_apple_wallet=bool(card.apple_serial_number),
        ),
        "wallet_urls": wallet_urls,
        "already_enrolled": already_enrolled,
        "allow_cross_location": allow_cross_location,
        "enrolled_at_store_name": enrolled_at_store_name,
        "merchant_locations": location_list,
    }


# =============================================================================
# Authenticated Endpoints
# =============================================================================


@storefront_router.get("/loyalty/card")
def get_my_card(
    request: Request,
    customer: CustomerContext = Depends(get_current_customer_api),
    db: Session = Depends(get_db),
):
    """
    Get customer's loyalty card and program info.
    Returns card details, program info, and available rewards.
    """
    store = getattr(request.state, "store", None)
    if not store:
        raise StoreNotFoundException("context", identifier_type="subdomain")

    logger.debug(f"Getting loyalty card for customer {customer.id}")

    # Get program
    program = program_service.get_program_by_store(db, store.id)
    if not program:
        return {"card": None, "program": None, "locations": []}

    # Look up card by customer ID and merchant
    card = card_service.get_card_by_customer_and_merchant(
        db,
        customer_id=customer.id,
        merchant_id=program.merchant_id,
    )

    if not card:
        return {"card": None, "program": None, "locations": []}

    # Get merchant locations
    locations = program_service.get_merchant_locations(db, program.merchant_id)

    program_response = ProgramResponse.model_validate(program)
    program_response.is_stamps_enabled = program.is_stamps_enabled
    program_response.is_points_enabled = program.is_points_enabled
    program_response.display_name = program.display_name

    wallet_urls = wallet_service.get_add_to_wallet_urls(db, card)

    return {
        "card": CardResponse(
            id=card.id,
            card_number=card.card_number,
            customer_id=card.customer_id,
            merchant_id=card.merchant_id,
            program_id=card.program_id,
            enrolled_at_store_id=card.enrolled_at_store_id,
            stamp_count=card.stamp_count,
            stamps_target=program.stamps_target,
            stamps_until_reward=max(0, program.stamps_target - card.stamp_count),
            total_stamps_earned=card.total_stamps_earned,
            stamps_redeemed=card.stamps_redeemed,
            points_balance=card.points_balance,
            total_points_earned=card.total_points_earned,
            points_redeemed=card.points_redeemed,
            is_active=card.is_active,
            created_at=card.created_at,
            has_google_wallet=bool(card.google_object_id),
            has_apple_wallet=bool(card.apple_serial_number),
        ),
        "program": program_response,
        "locations": [{"id": v.id, "name": v.name} for v in locations],
        "wallet_urls": wallet_urls,
    }


@storefront_router.get("/loyalty/transactions")
def get_my_transactions(
    request: Request,
    skip: int = Query(0, ge=0),
    limit: int = Query(20, ge=1, le=100),
    customer: CustomerContext = Depends(get_current_customer_api),
    db: Session = Depends(get_db),
):
    """
    Get customer's loyalty transaction history.
    """
    store = getattr(request.state, "store", None)
    if not store:
        raise StoreNotFoundException("context", identifier_type="subdomain")

    logger.debug(f"Getting transactions for customer {customer.id}")

    # Get program
    program = program_service.get_program_by_store(db, store.id)
    if not program:
        return {"transactions": [], "total": 0}

    # Get card
    card = card_service.get_card_by_customer_and_merchant(
        db,
        customer_id=customer.id,
        merchant_id=program.merchant_id,
    )

    if not card:
        return {"transactions": [], "total": 0}

    # Get transactions with store names
    tx_responses, total = card_service.get_customer_transactions_with_store_names(
        db, card.id, skip=skip, limit=limit
    )

    return {"transactions": tx_responses, "total": total}
