"""Stripe SDK client wrapper (B-2.7).

Lazy-initializes stripe.api_key from STRIPE_SECRET_KEY at first use, so the
app can boot without Stripe credentials (CI, dev, contributor branches).
Raises StripeNotConfiguredError if a Stripe API call is attempted without
the key set.

This module is intentionally thin: it owns the stripe.* call surface used by
B-2.7 (Checkout) and is reused by B-2.8 (webhook signature verification).
No subscription state is persisted here — the webhook is the source of truth
for `user.subscription_status`. The only User mutation is `stripe_customer_id`
(identity, not state).
"""
import os
from typing import List

import stripe


class StripeNotConfiguredError(RuntimeError):
    """Raised when STRIPE_SECRET_KEY (or a plan Price ID) is missing at call time."""


def is_stripe_configured() -> bool:
    """Return True if STRIPE_SECRET_KEY is set in the environment."""
    return bool(os.environ.get('STRIPE_SECRET_KEY'))


def _ensure_configured() -> None:
    """Lazy-initialize stripe.api_key. Raises if STRIPE_SECRET_KEY is missing."""
    if not is_stripe_configured():
        raise StripeNotConfiguredError(
            'STRIPE_SECRET_KEY is not set. Configure it before using billing.'
        )
    if not stripe.api_key:
        stripe.api_key = os.environ['STRIPE_SECRET_KEY']


def get_or_create_customer(user) -> str:
    """Return the Stripe customer ID for `user`, creating one if needed.

    Persists the Stripe customer ID on user.stripe_customer_id so subsequent
    checkouts (and the webhook) can correlate Stripe events back to the user.
    """
    from src.database import db
    _ensure_configured()
    if user.stripe_customer_id:
        return user.stripe_customer_id

    customer = stripe.Customer.create(
        email=user.email,
        name=(user.name or user.username),
        metadata={
            'dictia_user_id': str(user.id),
            'dictia_username': user.username,
        },
    )
    user.stripe_customer_id = customer.id
    db.session.commit()
    return customer.id


def create_checkout_session(
    plan_slug: str,
    period: str,
    user,
    success_url: str,
    cancel_url: str,
):
    """Create a Stripe Checkout Session for the given plan + period.

    Configuration applied:
      - mode='subscription' (recurring)
      - currency='cad'
      - automatic_tax.enabled=true (Stripe applies TPS 5% + TVQ 9.975%)
      - billing_address_collection='required' (needed for Tax)
      - allow_promotion_codes=true
      - Apple/Google Pay are auto-enabled for card payments in Stripe Dashboard
      - Hardware plans (8/16) include a one-time setup line item AND the
        recurring subscription line item.

    The success_url is decorated with `?session_id={CHECKOUT_SESSION_ID}` so
    the success page can optionally surface the session id (analytics).
    """
    from src.billing.plans import VALID_PERIODS, get_plan

    _ensure_configured()
    plan = get_plan(plan_slug)
    if plan is None:
        raise ValueError(f'Unknown plan: {plan_slug!r}')
    if period not in VALID_PERIODS:
        raise ValueError(
            f'Invalid period: {period!r} (expected one of {VALID_PERIODS})'
        )
    if not plan.is_configured():
        raise StripeNotConfiguredError(
            f'Stripe Price IDs for {plan_slug!r} are not set in environment.'
        )

    customer_id = get_or_create_customer(user)

    line_items: List[dict] = []
    # One-time setup fee for hardware plans (DictIA 8 / DictIA 16)
    if plan.has_setup_fee:
        setup_id = plan.setup_price_id()
        if setup_id:
            line_items.append({'price': setup_id, 'quantity': 1})
    # Recurring subscription
    line_items.append({
        'price': plan.price_id_for_period(period),
        'quantity': 1,
    })

    # Inject CHECKOUT_SESSION_ID placeholder while preserving any existing query string
    decorated_success_url = success_url + (
        '&' if '?' in success_url else '?'
    ) + 'session_id={CHECKOUT_SESSION_ID}'

    metadata = {
        'dictia_user_id': str(user.id),
        'dictia_plan_slug': plan_slug,
        'dictia_period': period,
    }

    return stripe.checkout.Session.create(
        mode='subscription',
        customer=customer_id,
        line_items=line_items,
        success_url=decorated_success_url,
        cancel_url=cancel_url,
        automatic_tax={'enabled': True},
        currency='cad',
        billing_address_collection='required',
        customer_update={'address': 'auto', 'name': 'auto'},
        allow_promotion_codes=True,
        metadata=metadata,
        # Webhook (B-2.8) reads metadata off the subscription, not the session
        subscription_data={'metadata': metadata},
    )
