"""Stripe subscription billing for Vastian cloud.

Creates Checkout Sessions for Standard/Premium plans and handles webhooks
to update user tiers.
"""

from __future__ import annotations

import logging
import os
import secrets

logger = logging.getLogger(__name__)

# Price ID mapping: plan_key -> env var name
PLAN_TO_ENV: dict[str, str] = {
    "standard_monthly": "STRIPE_PRICE_STANDARD_MONTHLY",
    "standard_annual": "STRIPE_PRICE_STANDARD_ANNUAL",
    "premium_monthly": "STRIPE_PRICE_PREMIUM_MONTHLY",
    "premium_annual": "STRIPE_PRICE_PREMIUM_ANNUAL",
}

# Price ID -> tier
PRICE_TO_TIER: dict[str, str] = {}  # Populated at runtime from env


def _price_to_tier(price_id: str) -> str:
    """Map a Stripe price ID to Vastian tier (standard or premium)."""
    if not PRICE_TO_TIER:
        for plan, env_key in PLAN_TO_ENV.items():
            pid = os.environ.get(env_key, "")
            if pid:
                PRICE_TO_TIER[pid] = "standard" if plan.startswith("standard") else "premium"
    return PRICE_TO_TIER.get(price_id, "open")


def get_price_id(plan: str) -> str | None:
    """Return Stripe price ID for plan, or None if not configured."""
    env_key = PLAN_TO_ENV.get(plan)
    if not env_key:
        return None
    return os.environ.get(env_key) or None


def create_checkout_session(
    *,
    price_id: str,
    success_url: str,
    cancel_url: str,
    customer_email: str | None = None,
    client_reference_id: str | None = None,
) -> str | None:
    """Create a Stripe Checkout Session for subscription. Returns session URL or None on error."""
    import stripe

    secret = os.environ.get("STRIPE_SECRET_KEY")
    if not secret:
        logger.warning("STRIPE_SECRET_KEY not set; cannot create checkout")
        return None

    stripe.api_key = secret

    params: dict = {
        "mode": "subscription",
        "line_items": [{"price": price_id, "quantity": 1}],
        "success_url": success_url,
        "cancel_url": cancel_url,
        "allow_promotion_codes": True,
    }
    if customer_email:
        params["customer_email"] = customer_email
    if client_reference_id:
        params["client_reference_id"] = client_reference_id

    try:
        session = stripe.checkout.Session.create(**params)
        return session.url
    except Exception as e:
        logger.exception("Stripe checkout creation failed: %s", e)
        return None


def handle_webhook(payload: bytes, sig_header: str) -> tuple[bool, str]:
    """Verify and process Stripe webhook. Returns (ok, message)."""
    import stripe

    secret = os.environ.get("STRIPE_SECRET_KEY")
    webhook_secret = os.environ.get("STRIPE_WEBHOOK_SECRET")
    if not secret or not webhook_secret:
        return False, "Stripe not configured"

    stripe.api_key = secret

    try:
        event = stripe.Webhook.construct_event(payload, sig_header, webhook_secret)
    except ValueError as e:
        return False, f"Invalid payload: {e}"
    except Exception as e:
        return False, f"Webhook signature verification failed: {e}"

    from vastian.server.auth import (
        get_user_by_email,
        get_user_by_stripe_customer_id,
        register_user,
        set_stripe_customer_id,
        update_user_tier,
    )

    if event.type == "customer.subscription.created":
        sub = event.data.object
        customer_id = getattr(sub, "customer", None) or (
            sub.get("customer") if isinstance(sub, dict) else None
        )
        items = getattr(sub, "items", None)
        if items is None and isinstance(sub, dict):
            items = sub.get("items")
        data = getattr(items, "data", None) if items else None
        if data is None and isinstance(items, dict):
            data = items.get("data", [])
        item = (list(data)[0] if data else None) if data else None
        price = getattr(item, "price", None) if item else None
        if price is None and isinstance(item, dict):
            price = item.get("price", {})
        price_id = getattr(price, "id", None) if price else None
        if price_id is None and isinstance(price, dict):
            price_id = price.get("id")
        if not price_id:
            return True, "No price in subscription"
        tier = _price_to_tier(price_id)

        # Find user: client_reference_id from checkout, or by stripe_customer_id, or by email
        user_id = getattr(sub, "client_reference_id", None) or (
            sub.get("client_reference_id") if isinstance(sub, dict) else None
        )
        if user_id:
            set_stripe_customer_id(user_id, customer_id)
            update_user_tier(user_id, tier)
            return True, f"Updated user {user_id} to {tier}"

        uid, _ = get_user_by_stripe_customer_id(customer_id)
        if uid:
            update_user_tier(uid, tier)
            return True, f"Updated user {uid} to {tier}"

        # New customer: retrieve email from Stripe customer, create user if needed
        try:
            customer = stripe.Customer.retrieve(customer_id)
            email = getattr(customer, "email", None) or (
                customer.get("email") if isinstance(customer, dict) else None
            )
        except Exception:
            email = None
        if email:
            uid, u = get_user_by_email(email)
            if uid:
                set_stripe_customer_id(uid, customer_id)
                update_user_tier(uid, tier)
                return True, f"Linked and updated user {uid} to {tier}"
            # Create new user with temp password (they'll use passphrase or reset)
            uid, err = register_user(
                email,
                secrets.token_urlsafe(16),
                name=email.split("@")[0],
                tier=tier,
                storage_mode="zo",
            )
            if uid:
                set_stripe_customer_id(uid, customer_id)
                return True, f"Created user {uid} with tier {tier}"
        return True, "Subscription created (no user linked)"

    if event.type == "customer.subscription.updated":
        sub = event.data.object
        status = getattr(sub, "status", None) or (
            sub.get("status") if isinstance(sub, dict) else ""
        )
        if status not in ("active", "trialing"):
            return True, "Subscription not active, skipping"
        customer_id = getattr(sub, "customer", None) or (
            sub.get("customer") if isinstance(sub, dict) else None
        )
        items = getattr(sub, "items", None) or (sub.get("items") if isinstance(sub, dict) else None)
        data = getattr(items, "data", []) if items else []
        if not data and isinstance(items, dict):
            data = items.get("data", [])
        item = data[0] if data else None
        price = getattr(item, "price", None) if item else None
        if price is None and isinstance(item, dict):
            price = item.get("price", {})
        price_id = (
            getattr(price, "id", None)
            if price
            else (price.get("id") if isinstance(price, dict) else None)
        )
        if not price_id:
            return True, "No price"
        tier = _price_to_tier(price_id)
        uid, _ = get_user_by_stripe_customer_id(customer_id)
        if uid:
            update_user_tier(uid, tier)
            return True, f"Updated user {uid} to {tier}"
        return True, "No user linked"

    if event.type == "customer.subscription.deleted":
        sub = event.data.object
        customer_id = getattr(sub, "customer", None) or (
            sub.get("customer") if isinstance(sub, dict) else None
        )
        uid, _ = get_user_by_stripe_customer_id(customer_id)
        if uid:
            update_user_tier(uid, "open")
            return True, f"Downgraded user {uid} to open"
        return True, "No user linked"

    return True, f"Ignored event type {event.type}"
