"""
Stripe Webhook Handlers (Reference Implementation)

These are reference implementations showing how to use the typed dispatcher.
For production use, copy these to your Django app and customize.

Usage in Django app:
    # apps/core/billing/webhooks.py
    from lightwave.integrations.stripe.webhooks import register_handler
    from lightwave.integrations.stripe.contracts import CheckoutSessionCompletedEvent

    @register_handler("checkout.session.completed")
    def handle_checkout_completed(event: CheckoutSessionCompletedEvent) -> None:
        session = event.data.object  # Typed dict
        customer_id = session.get("customer")
        subscription_id = session.get("subscription")
        # ... business logic
"""

from __future__ import annotations

import logging

from lightwave.integrations.stripe.contracts import (
    CheckoutSessionCompletedEvent,
    CheckoutSessionExpiredEvent,
    CustomerCreatedEvent,
    CustomerDeletedEvent,
    CustomerSubscriptionCreatedEvent,
    CustomerSubscriptionDeletedEvent,
    CustomerSubscriptionPausedEvent,
    CustomerSubscriptionResumedEvent,
    CustomerSubscriptionTrialWillEndEvent,
    CustomerSubscriptionUpdatedEvent,
    CustomerUpdatedEvent,
    InvoiceCreatedEvent,
    InvoiceFinalizedEvent,
    InvoicePaidEvent,
    InvoicePaymentFailedEvent,
    InvoicePaymentSucceededEvent,
    InvoiceUpcomingEvent,
    PaymentIntentPaymentFailedEvent,
    PaymentIntentSucceededEvent,
    PaymentMethodAttachedEvent,
    PaymentMethodDetachedEvent,
    PriceCreatedEvent,
    PriceDeletedEvent,
    PriceUpdatedEvent,
    ProductCreatedEvent,
    ProductDeletedEvent,
    ProductUpdatedEvent,
)
from lightwave.integrations.stripe.webhooks.dispatcher import register_handler

logger = logging.getLogger(__name__)


# =============================================================================
# Checkout Session Handlers
# =============================================================================


@register_handler("checkout.session.completed")
def handle_checkout_session_completed(event: CheckoutSessionCompletedEvent) -> None:
    """
    Handle successful checkout session completion.

    This fires when a customer completes the Stripe Checkout flow.
    Use this to:
    - Create/update subscription records
    - Provision access
    - Send welcome emails
    """
    session = event.data.object
    customer_id = session.get("customer")
    subscription_id = session.get("subscription")
    payment_status = session.get("payment_status")
    metadata = session.get("metadata", {})

    logger.info(
        f"Checkout completed: customer={customer_id}, " f"subscription={subscription_id}, status={payment_status}"
    )

    # Extract custom metadata (used in TODO implementation)
    _tenant_id = metadata.get("tenant_id")
    _price_lookup_key = metadata.get("price_lookup_key")

    if payment_status == "paid":
        # TODO: Implement subscription provisioning
        # - Look up tenant by _tenant_id
        # - Create/update TenantBilling record
        # - Provision domain access based on _price_lookup_key
        pass


@register_handler("checkout.session.expired")
def handle_checkout_session_expired(event: CheckoutSessionExpiredEvent) -> None:
    """
    Handle checkout session expiration.

    This fires when a checkout session expires without completion.
    Use this to clean up any pending state.
    """
    session = event.data.object
    logger.info(f"Checkout expired: {session.get('id')}")


# =============================================================================
# Customer Handlers
# =============================================================================


@register_handler("customer.created")
def handle_customer_created(event: CustomerCreatedEvent) -> None:
    """
    Handle new customer creation in Stripe.

    This fires when a new customer is created via API or Checkout.
    """
    customer = event.data.object
    logger.info(f"Customer created: {customer.get('id')} - {customer.get('email')}")


@register_handler("customer.updated")
def handle_customer_updated(event: CustomerUpdatedEvent) -> None:
    """
    Handle customer updates.

    This fires when customer details change.
    """
    customer = event.data.object
    logger.info(f"Customer updated: {customer.get('id')}")


@register_handler("customer.deleted")
def handle_customer_deleted(event: CustomerDeletedEvent) -> None:
    """
    Handle customer deletion.

    This fires when a customer is deleted in Stripe.
    """
    customer = event.data.object
    logger.warning(f"Customer deleted: {customer.get('id')}")


# =============================================================================
# Subscription Handlers
# =============================================================================


@register_handler("customer.subscription.created")
def handle_subscription_created(event: CustomerSubscriptionCreatedEvent) -> None:
    """
    Handle new subscription creation.

    This fires when a subscription is created (usually via Checkout).
    """
    subscription = event.data.object
    logger.info(f"Subscription created: {subscription.get('id')} " f"status={subscription.get('status')}")

    # TODO: Implement subscription sync
    # - Create local subscription record
    # - Set up domain access


@register_handler("customer.subscription.updated")
def handle_subscription_updated(event: CustomerSubscriptionUpdatedEvent) -> None:
    """
    Handle subscription updates.

    This fires on plan changes, cancellation requests, renewals, etc.
    """
    subscription = event.data.object
    status = subscription.get("status")
    cancel_at_period_end = subscription.get("cancel_at_period_end", False)

    logger.info(
        f"Subscription updated: {subscription.get('id')} "
        f"status={status}, cancel_at_period_end={cancel_at_period_end}"
    )

    # TODO: Handle different states
    # - active: Subscription is active
    # - past_due: Payment failed but grace period
    # - canceled: Subscription ended
    # - unpaid: Payment failed, grace period ended


@register_handler("customer.subscription.deleted")
def handle_subscription_deleted(event: CustomerSubscriptionDeletedEvent) -> None:
    """
    Handle subscription deletion/cancellation.

    This fires when a subscription is fully canceled.
    """
    subscription = event.data.object
    logger.warning(f"Subscription deleted: {subscription.get('id')} " f"customer={subscription.get('customer')}")

    # TODO: Implement access revocation
    # - Update local subscription status
    # - Remove domain access
    # - Optionally retain data for grace period


# =============================================================================
# Invoice Handlers
# =============================================================================


@register_handler("invoice.paid")
def handle_invoice_paid(event: InvoicePaidEvent) -> None:
    """
    Handle successful invoice payment.

    This fires when an invoice is paid (subscription renewal, etc.).
    """
    invoice = event.data.object
    subscription_id = invoice.get("subscription")
    amount_paid = invoice.get("amount_paid", 0)

    logger.info(f"Invoice paid: {invoice.get('id')} " f"subscription={subscription_id} amount={amount_paid}")

    # TODO: Update billing records
    # - Record payment
    # - Reset any payment failure state


@register_handler("invoice.payment_failed")
def handle_invoice_payment_failed(event: InvoicePaymentFailedEvent) -> None:
    """
    Handle failed invoice payment.

    This fires when a payment attempt fails.
    """
    invoice = event.data.object
    subscription_id = invoice.get("subscription")
    attempt_count = invoice.get("attempt_count", 0)

    logger.warning(
        f"Invoice payment failed: {invoice.get('id')} " f"subscription={subscription_id} attempt={attempt_count}"
    )

    # TODO: Handle payment failure
    # - Send notification to customer
    # - Update subscription status
    # - Consider grace period logic


@register_handler("invoice.created")
def handle_invoice_created(event: InvoiceCreatedEvent) -> None:
    """
    Handle invoice creation.

    This fires when a new invoice is created (draft state).
    """
    invoice = event.data.object
    logger.info(
        f"Invoice created: {invoice.get('id')} " f"customer={invoice.get('customer')} status={invoice.get('status')}"
    )


@register_handler("invoice.finalized")
def handle_invoice_finalized(event: InvoiceFinalizedEvent) -> None:
    """
    Handle invoice finalization.

    This fires when an invoice is finalized and ready for payment.
    """
    invoice = event.data.object
    logger.info(f"Invoice finalized: {invoice.get('id')} " f"amount_due={invoice.get('amount_due')}")


@register_handler("invoice.payment_succeeded")
def handle_invoice_payment_succeeded(event: InvoicePaymentSucceededEvent) -> None:
    """
    Handle successful invoice payment.

    Similar to invoice.paid but fires specifically on payment success.
    """
    invoice = event.data.object
    logger.info(f"Invoice payment succeeded: {invoice.get('id')} " f"amount_paid={invoice.get('amount_paid')}")


@register_handler("invoice.upcoming")
def handle_invoice_upcoming(event: InvoiceUpcomingEvent) -> None:
    """
    Handle upcoming invoice notification.

    This fires ~3 days before a subscription renews.
    Use this to notify customers of upcoming charges.
    """
    invoice = event.data.object
    logger.info(f"Invoice upcoming: customer={invoice.get('customer')} " f"amount_due={invoice.get('amount_due')}")

    # TODO: Send upcoming charge notification to customer


# =============================================================================
# Subscription Lifecycle Handlers
# =============================================================================


@register_handler("customer.subscription.paused")
def handle_subscription_paused(event: CustomerSubscriptionPausedEvent) -> None:
    """
    Handle subscription pause.

    This fires when a subscription is paused (if pause collection is enabled).
    """
    subscription = event.data.object
    logger.info(f"Subscription paused: {subscription.get('id')} " f"customer={subscription.get('customer')}")

    # TODO: Update local subscription status to paused
    # TODO: Optionally restrict access during pause


@register_handler("customer.subscription.resumed")
def handle_subscription_resumed(event: CustomerSubscriptionResumedEvent) -> None:
    """
    Handle subscription resume.

    This fires when a paused subscription is resumed.
    """
    subscription = event.data.object
    logger.info(f"Subscription resumed: {subscription.get('id')} " f"customer={subscription.get('customer')}")

    # TODO: Update local subscription status to active
    # TODO: Restore access


@register_handler("customer.subscription.trial_will_end")
def handle_subscription_trial_will_end(event: CustomerSubscriptionTrialWillEndEvent) -> None:
    """
    Handle trial ending notification.

    This fires 3 days before a trial period ends.
    Use this to:
    - Notify customer their trial is ending
    - Prompt them to add payment method
    - Offer discounts to convert
    """
    subscription = event.data.object
    trial_end = subscription.get("trial_end")

    logger.info(
        f"Trial ending soon: {subscription.get('id')} " f"customer={subscription.get('customer')} trial_end={trial_end}"
    )

    # TODO: Send trial ending notification
    # TODO: Check if payment method is attached


# =============================================================================
# Payment Intent Handlers
# =============================================================================


@register_handler("payment_intent.succeeded")
def handle_payment_intent_succeeded(event: PaymentIntentSucceededEvent) -> None:
    """
    Handle successful payment intent.

    This fires when a payment is successfully processed.
    For subscriptions, invoice.paid is usually more useful.
    """
    payment_intent = event.data.object
    logger.info(
        f"Payment intent succeeded: {payment_intent.get('id')} "
        f"amount={payment_intent.get('amount')} "
        f"customer={payment_intent.get('customer')}"
    )


@register_handler("payment_intent.payment_failed")
def handle_payment_intent_failed(event: PaymentIntentPaymentFailedEvent) -> None:
    """
    Handle failed payment intent.

    This fires when a payment attempt fails.
    """
    payment_intent = event.data.object
    error = payment_intent.get("last_payment_error", {})

    logger.warning(f"Payment intent failed: {payment_intent.get('id')} " f"error={error.get('message')}")

    # TODO: Handle payment failure
    # - Notify customer
    # - Suggest different payment method


# =============================================================================
# Payment Method Handlers
# =============================================================================


@register_handler("payment_method.attached")
def handle_payment_method_attached(event: PaymentMethodAttachedEvent) -> None:
    """
    Handle payment method attachment.

    This fires when a payment method is attached to a customer.
    """
    payment_method = event.data.object
    logger.info(
        f"Payment method attached: {payment_method.get('id')} "
        f"type={payment_method.get('type')} "
        f"customer={payment_method.get('customer')}"
    )


@register_handler("payment_method.detached")
def handle_payment_method_detached(event: PaymentMethodDetachedEvent) -> None:
    """
    Handle payment method detachment.

    This fires when a payment method is removed from a customer.
    """
    payment_method = event.data.object
    logger.info(f"Payment method detached: {payment_method.get('id')} " f"type={payment_method.get('type')}")


# =============================================================================
# Product Catalog Handlers (for sync)
# =============================================================================


@register_handler("product.created")
def handle_product_created(event: ProductCreatedEvent) -> None:
    """
    Handle product creation in Stripe.

    Use this to sync products to local BillingProduct records.
    """
    product = event.data.object
    logger.info(f"Product created: {product.get('id')} " f"name={product.get('name')} active={product.get('active')}")

    # TODO: Create local BillingProduct record
    # TODO: Sync metadata (tier, domain_access, etc.)


@register_handler("product.updated")
def handle_product_updated(event: ProductUpdatedEvent) -> None:
    """
    Handle product update in Stripe.

    Use this to sync product changes to local records.
    """
    product = event.data.object
    logger.info(f"Product updated: {product.get('id')} " f"name={product.get('name')} active={product.get('active')}")

    # TODO: Update local BillingProduct record
    # TODO: Re-sync metadata


@register_handler("product.deleted")
def handle_product_deleted(event: ProductDeletedEvent) -> None:
    """
    Handle product deletion in Stripe.

    Note: Stripe doesn't allow deleting products with active prices.
    """
    product = event.data.object
    logger.warning(f"Product deleted: {product.get('id')}")

    # TODO: Mark local BillingProduct as inactive


@register_handler("price.created")
def handle_price_created(event: PriceCreatedEvent) -> None:
    """
    Handle price creation in Stripe.

    Use this to sync prices for display in pricing tables.
    """
    price = event.data.object
    logger.info(
        f"Price created: {price.get('id')} "
        f"product={price.get('product')} "
        f"unit_amount={price.get('unit_amount')}"
    )

    # TODO: Sync to djstripe via Price.sync_from_stripe_data


@register_handler("price.updated")
def handle_price_updated(event: PriceUpdatedEvent) -> None:
    """
    Handle price update in Stripe.
    """
    price = event.data.object
    logger.info(f"Price updated: {price.get('id')} " f"active={price.get('active')}")

    # TODO: Sync changes to djstripe


@register_handler("price.deleted")
def handle_price_deleted(event: PriceDeletedEvent) -> None:
    """
    Handle price deletion/archival in Stripe.
    """
    price = event.data.object
    logger.warning(f"Price deleted: {price.get('id')}")

    # TODO: Mark price as inactive locally
