"""
Stripe subscription integration.

Tiers:
  - terminal: $30/month — Terminal access
  - api:      $60/month — API & SDK access (includes terminal)

Referral discounts applied as Stripe coupon (20% off forever).
"""

import stripe
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select

from app.config import get_settings
from app.modules.user.models import User
from app.core.exceptions import BadRequestError

settings = get_settings()

# Price IDs are configured in Stripe Dashboard and stored as env vars
# Set these after creating products in Stripe
TIER_PRICES = {
    "terminal": "STRIPE_PRICE_TERMINAL",   # env var name
    "api": "STRIPE_PRICE_API",             # env var name
}

TIER_AMOUNTS = {
    "terminal": 3000,  # $30.00
    "api": 6000,       # $60.00
}

REFERRAL_DISCOUNT_PERCENT = 20
REFERRAL_REVENUE_SHARE_PERCENT = 50

# Referral code -> username mapping
REFERRAL_CODES = {
    "august":  "august",
    "doji":    "dojionbase",
    "jake":    "jakeonbase",
    "swarm":   "eric",
    "harry":   "harry",
    "turbine": "woz",
}


def get_stripe():
    """Initialize stripe with secret key."""
    if not settings.STRIPE_SECRET_KEY:
        raise BadRequestError("Stripe is not configured on this server")
    stripe.api_key = settings.STRIPE_SECRET_KEY
    return stripe


def normalize_referral_code(code: str) -> str | None:
    """Normalize referral code (case-insensitive) and return canonical lowercase."""
    if not code:
        return None
    normalized = code.strip().lower()
    if normalized in REFERRAL_CODES:
        return normalized
    return None


async def get_or_create_customer(db: AsyncSession, user: User) -> str:
    """Get or create a Stripe customer for a user."""
    s = get_stripe()
    if user.stripe_customer_id:
        return user.stripe_customer_id

    customer = s.Customer.create(
        email=user.email,
        metadata={"user_id": str(user.id), "username": user.username},
    )
    user.stripe_customer_id = customer.id
    await db.commit()
    return customer.id


async def get_or_create_referral_coupon(s) -> str:
    """Get or create the 20% referral discount coupon in Stripe."""
    coupon_id = "luckyst_referral_20"
    try:
        s.Coupon.retrieve(coupon_id)
    except (s.error.InvalidRequestError if hasattr(s, 'error') else s.InvalidRequestError):
        s.Coupon.create(
            id=coupon_id,
            percent_off=REFERRAL_DISCOUNT_PERCENT,
            duration="forever",
            name="LuckySt Referral — 20% Off",
        )
    return coupon_id


async def create_checkout_session(
    db: AsyncSession,
    user: User,
    tier: str,
    referral_code: str | None = None,
    success_url: str = "",
    cancel_url: str = "",
) -> dict:
    """Create a Stripe Checkout session for subscription."""
    if tier not in TIER_PRICES:
        raise BadRequestError(f"Invalid tier: {tier}. Must be 'terminal' or 'api'")

    s = get_stripe()
    customer_id = await get_or_create_customer(db, user)

    price_id = getattr(settings, TIER_PRICES[tier], None) or ""
    if not price_id:
        raise BadRequestError(
            f"Stripe price not configured for tier '{tier}'. "
            f"Set {TIER_PRICES[tier]} env var after creating the product in Stripe Dashboard."
        )

    checkout_params = {
        "customer": customer_id,
        "payment_method_types": ["card"],
        "line_items": [{"price": price_id, "quantity": 1}],
        "mode": "subscription",
        "success_url": success_url or f"{settings.APP_BASE_URL}/account?session_id={{CHECKOUT_SESSION_ID}}",
        "cancel_url": cancel_url or f"{settings.APP_BASE_URL}/account",
        "metadata": {"user_id": str(user.id), "tier": tier},
    }

    # Apply referral discount
    validated_code = normalize_referral_code(referral_code) if referral_code else None
    if validated_code:
        coupon_id = await get_or_create_referral_coupon(s)
        checkout_params["discounts"] = [{"coupon": coupon_id}]
        checkout_params["metadata"]["referral_code"] = validated_code

        # Store referral on user if not already set
        if not user.referred_by:
            user.referred_by = validated_code
            await db.commit()

    session = s.checkout.Session.create(**checkout_params)
    return {"checkout_url": session.url, "session_id": session.id}


async def create_billing_portal(db: AsyncSession, user: User) -> dict:
    """Create a Stripe billing portal session for managing subscription."""
    s = get_stripe()
    if not user.stripe_customer_id:
        raise BadRequestError("No active subscription found")

    portal = s.billing_portal.Session.create(
        customer=user.stripe_customer_id,
        return_url=f"{settings.APP_BASE_URL}/account",
    )
    return {"portal_url": portal.url}


async def handle_webhook_event(db: AsyncSession, event: dict) -> None:
    """Process Stripe webhook events."""
    event_type = event.get("type", "")
    data = event.get("data", {}).get("object", {})

    if event_type == "checkout.session.completed":
        await _handle_checkout_completed(db, data)
    elif event_type in (
        "customer.subscription.updated",
        "customer.subscription.deleted",
    ):
        await _handle_subscription_change(db, data)


async def _handle_checkout_completed(db: AsyncSession, session_data: dict) -> None:
    """Activate subscription after successful checkout."""
    user_id = session_data.get("metadata", {}).get("user_id")
    tier = session_data.get("metadata", {}).get("tier")
    subscription_id = session_data.get("subscription")

    if not user_id or not tier:
        return

    result = await db.execute(select(User).where(User.id == int(user_id)))
    user = result.scalar_one_or_none()
    if not user:
        return

    user.subscription_tier = tier
    user.stripe_subscription_id = subscription_id

    # Store referral code if present
    referral_code = session_data.get("metadata", {}).get("referral_code")
    if referral_code and not user.referred_by:
        user.referred_by = referral_code

    await db.commit()


async def _handle_subscription_change(db: AsyncSession, sub_data: dict) -> None:
    """Handle subscription update or cancellation."""
    customer_id = sub_data.get("customer")
    if not customer_id:
        return

    result = await db.execute(
        select(User).where(User.stripe_customer_id == customer_id)
    )
    user = result.scalar_one_or_none()
    if not user:
        return

    status = sub_data.get("status", "")
    if status in ("active", "trialing"):
        # Subscription is active — check which tier
        items = sub_data.get("items", {}).get("data", [])
        if items:
            price_id = items[0].get("price", {}).get("id", "")
            if price_id == settings.STRIPE_PRICE_API:
                user.subscription_tier = "api"
            else:
                user.subscription_tier = "terminal"
        user.stripe_subscription_id = sub_data.get("id")
    else:
        # Cancelled, past_due, etc.
        user.subscription_tier = None
        user.stripe_subscription_id = None

    await db.commit()


def get_referral_info(code: str | None) -> dict | None:
    """Get referral code info."""
    if not code:
        return None
    normalized = normalize_referral_code(code)
    if not normalized:
        return None
    return {
        "code": normalized,
        "owner_username": REFERRAL_CODES[normalized],
        "discount_percent": REFERRAL_DISCOUNT_PERCENT,
        "revenue_share_percent": REFERRAL_REVENUE_SHARE_PERCENT,
    }
