"""
Stripe webhook payload schemas.

These schemas validate incoming webhook events from Stripe.
Stripe sends events for payment lifecycle changes, subscription
updates, and other account activities.

Stripe Webhook Documentation:
https://stripe.com/docs/webhooks

Usage:
    from lightwave.schema.pydantic.contracts.webhooks.stripe import StripeEventSchema

    @router.post("/webhooks/stripe/")
    def handle_stripe_webhook(request):
        # Verify signature first (see Stripe docs)
        event = StripeEventSchema.model_validate_json(request.body)

        if event.type == "checkout.session.completed":
            session = StripeCheckoutSession(**event.data["object"])
            handle_checkout(session)
        elif event.type == "invoice.paid":
            invoice = StripeInvoice(**event.data["object"])
            handle_invoice_paid(invoice)
"""

from datetime import datetime
from typing import Any, Literal

from pydantic import Field

from lightwave.schema.pydantic.core.base import LightwaveBaseSchema

# =============================================================================
# Common Stripe Event Types
# =============================================================================

StripeEventType = Literal[
    # Checkout
    "checkout.session.completed",
    "checkout.session.expired",
    "checkout.session.async_payment_succeeded",
    "checkout.session.async_payment_failed",
    # Customer
    "customer.created",
    "customer.updated",
    "customer.deleted",
    "customer.subscription.created",
    "customer.subscription.updated",
    "customer.subscription.deleted",
    "customer.subscription.paused",
    "customer.subscription.resumed",
    "customer.subscription.trial_will_end",
    # Invoice
    "invoice.created",
    "invoice.finalized",
    "invoice.paid",
    "invoice.payment_failed",
    "invoice.payment_action_required",
    "invoice.upcoming",
    "invoice.marked_uncollectible",
    "invoice.voided",
    # Payment Intent
    "payment_intent.succeeded",
    "payment_intent.processing",
    "payment_intent.payment_failed",
    "payment_intent.canceled",
    "payment_intent.requires_action",
    # Payment Method
    "payment_method.attached",
    "payment_method.detached",
    "payment_method.updated",
    # Charge
    "charge.succeeded",
    "charge.failed",
    "charge.refunded",
    "charge.dispute.created",
    "charge.dispute.updated",
    "charge.dispute.closed",
    # Subscription Schedule
    "subscription_schedule.created",
    "subscription_schedule.updated",
    "subscription_schedule.released",
    "subscription_schedule.canceled",
    # Product/Price
    "product.created",
    "product.updated",
    "product.deleted",
    "price.created",
    "price.updated",
    "price.deleted",
    # Account (Connect)
    "account.updated",
    "account.application.authorized",
    "account.application.deauthorized",
]


# =============================================================================
# Stripe Object Schemas
# =============================================================================


class StripeAddress(LightwaveBaseSchema):
    """Stripe address object."""

    city: str | None = None
    country: str | None = None
    line1: str | None = None
    line2: str | None = None
    postal_code: str | None = None
    state: str | None = None


class StripeCustomer(LightwaveBaseSchema):
    """
    Stripe Customer object.

    Represents a Stripe customer with billing details.
    """

    id: str = Field(..., description="Stripe customer ID (cus_xxx)")
    object: Literal["customer"] = "customer"
    email: str | None = Field(None, description="Customer email")
    name: str | None = Field(None, description="Customer name")
    phone: str | None = Field(None, description="Customer phone")
    address: StripeAddress | None = Field(None, description="Billing address")
    created: int = Field(..., description="Unix timestamp of creation")
    currency: str | None = Field(None, description="Default currency")
    default_source: str | None = Field(None, description="Default payment source ID")
    delinquent: bool = Field(False, description="Whether customer has unpaid invoices")
    description: str | None = Field(None, description="Customer description")
    livemode: bool = Field(..., description="Whether in live mode")
    metadata: dict[str, str] = Field(default_factory=dict, description="Custom metadata")


class StripeSubscription(LightwaveBaseSchema):
    """
    Stripe Subscription object.

    Represents an active subscription with billing cycle info.
    """

    id: str = Field(..., description="Stripe subscription ID (sub_xxx)")
    object: Literal["subscription"] = "subscription"
    customer: str = Field(..., description="Customer ID")
    status: Literal[
        "incomplete",
        "incomplete_expired",
        "trialing",
        "active",
        "past_due",
        "canceled",
        "unpaid",
        "paused",
    ] = Field(..., description="Subscription status")
    current_period_start: int = Field(..., description="Current period start (Unix)")
    current_period_end: int = Field(..., description="Current period end (Unix)")
    cancel_at_period_end: bool = Field(False, description="Cancel at period end")
    canceled_at: int | None = Field(None, description="When canceled (Unix)")
    ended_at: int | None = Field(None, description="When ended (Unix)")
    trial_start: int | None = Field(None, description="Trial start (Unix)")
    trial_end: int | None = Field(None, description="Trial end (Unix)")
    created: int = Field(..., description="Creation timestamp (Unix)")
    livemode: bool = Field(..., description="Whether in live mode")
    metadata: dict[str, str] = Field(default_factory=dict, description="Custom metadata")
    items: dict[str, Any] = Field(default_factory=dict, description="Subscription items")


class StripeInvoice(LightwaveBaseSchema):
    """
    Stripe Invoice object.

    Represents an invoice for a subscription or one-time charge.
    """

    id: str = Field(..., description="Stripe invoice ID (in_xxx)")
    object: Literal["invoice"] = "invoice"
    customer: str = Field(..., description="Customer ID")
    subscription: str | None = Field(None, description="Subscription ID if recurring")
    status: Literal[
        "draft",
        "open",
        "paid",
        "uncollectible",
        "void",
    ] = Field(..., description="Invoice status")
    amount_due: int = Field(..., description="Amount due in cents")
    amount_paid: int = Field(..., description="Amount paid in cents")
    amount_remaining: int = Field(..., description="Amount remaining in cents")
    currency: str = Field(..., description="Three-letter currency code")
    hosted_invoice_url: str | None = Field(None, description="URL to hosted invoice page")
    invoice_pdf: str | None = Field(None, description="URL to invoice PDF")
    created: int = Field(..., description="Creation timestamp (Unix)")
    due_date: int | None = Field(None, description="Due date (Unix)")
    paid_at: int | None = Field(None, description="When paid (Unix)")
    period_start: int = Field(..., description="Billing period start (Unix)")
    period_end: int = Field(..., description="Billing period end (Unix)")
    livemode: bool = Field(..., description="Whether in live mode")
    metadata: dict[str, str] = Field(default_factory=dict, description="Custom metadata")


class StripePaymentIntent(LightwaveBaseSchema):
    """
    Stripe PaymentIntent object.

    Represents a payment attempt and its lifecycle.
    """

    id: str = Field(..., description="Stripe payment intent ID (pi_xxx)")
    object: Literal["payment_intent"] = "payment_intent"
    customer: str | None = Field(None, description="Customer ID")
    amount: int = Field(..., description="Amount in cents")
    amount_received: int = Field(0, description="Amount received in cents")
    currency: str = Field(..., description="Three-letter currency code")
    status: Literal[
        "requires_payment_method",
        "requires_confirmation",
        "requires_action",
        "processing",
        "requires_capture",
        "canceled",
        "succeeded",
    ] = Field(..., description="Payment status")
    payment_method: str | None = Field(None, description="Payment method ID")
    receipt_email: str | None = Field(None, description="Receipt email")
    created: int = Field(..., description="Creation timestamp (Unix)")
    canceled_at: int | None = Field(None, description="When canceled (Unix)")
    cancellation_reason: str | None = Field(None, description="Cancellation reason")
    livemode: bool = Field(..., description="Whether in live mode")
    metadata: dict[str, str] = Field(default_factory=dict, description="Custom metadata")


class StripeCheckoutSession(LightwaveBaseSchema):
    """
    Stripe Checkout Session object.

    Represents a checkout session created via Stripe Checkout.
    """

    id: str = Field(..., description="Stripe session ID (cs_xxx)")
    object: Literal["checkout.session"] = "checkout.session"
    customer: str | None = Field(None, description="Customer ID")
    customer_email: str | None = Field(None, description="Customer email")
    mode: Literal["payment", "setup", "subscription"] = Field(..., description="Checkout mode")
    payment_status: Literal["paid", "unpaid", "no_payment_required"] = Field(..., description="Payment status")
    status: Literal["open", "complete", "expired"] = Field(..., description="Session status")
    amount_total: int | None = Field(None, description="Total amount in cents")
    currency: str | None = Field(None, description="Three-letter currency code")
    subscription: str | None = Field(None, description="Subscription ID if subscription mode")
    payment_intent: str | None = Field(None, description="Payment intent ID if payment mode")
    success_url: str = Field(..., description="Success redirect URL")
    cancel_url: str = Field(..., description="Cancel redirect URL")
    created: int = Field(..., description="Creation timestamp (Unix)")
    expires_at: int = Field(..., description="Expiration timestamp (Unix)")
    livemode: bool = Field(..., description="Whether in live mode")
    metadata: dict[str, str] = Field(default_factory=dict, description="Custom metadata")
    client_reference_id: str | None = Field(None, description="Client reference ID")


# =============================================================================
# Stripe Event Schema
# =============================================================================


class StripeEventSchema(LightwaveBaseSchema):
    """
    Root Stripe webhook event payload.

    All Stripe webhooks have this structure. The `data.object` field
    contains the event-specific payload (Customer, Invoice, etc.).

    Example payload:
        {
            "id": "evt_1234567890",
            "object": "event",
            "type": "checkout.session.completed",
            "created": 1704067200,
            "livemode": false,
            "data": {
                "object": {...}
            },
            "request": {
                "id": "req_xxx",
                "idempotency_key": null
            }
        }

    Usage:
        event = StripeEventSchema.model_validate_json(request.body)

        if event.type == "checkout.session.completed":
            session = StripeCheckoutSession(**event.data["object"])
            handle_checkout(session)
    """

    id: str = Field(..., description="Unique event identifier (evt_xxx)")
    object: Literal["event"] = Field("event", description="Object type")
    type: str = Field(..., description="Event type (e.g., 'checkout.session.completed')")
    created: int = Field(..., description="Event creation timestamp (Unix)")
    livemode: bool = Field(..., description="Whether in live mode")
    data: dict[str, Any] = Field(..., description="Event data containing 'object'")
    pending_webhooks: int = Field(0, description="Number of pending webhook deliveries")
    request: dict[str, Any] | None = Field(None, description="API request that triggered the event")
    api_version: str | None = Field(None, description="Stripe API version")

    @property
    def event_object(self) -> dict[str, Any]:
        """Get the event's main object from data.object."""
        return self.data.get("object", {})

    @property
    def created_datetime(self) -> datetime:
        """Get created as datetime."""
        return datetime.utcfromtimestamp(self.created)

    def is_type(self, *event_types: str) -> bool:
        """Check if event matches any of the given types."""
        return self.type in event_types

    def get_customer_id(self) -> str | None:
        """Extract customer ID from event object."""
        obj = self.event_object
        if "customer" in obj:
            customer = obj["customer"]
            return customer if isinstance(customer, str) else customer.get("id")
        return None

    def get_subscription_id(self) -> str | None:
        """Extract subscription ID from event object."""
        obj = self.event_object
        if "subscription" in obj:
            sub = obj["subscription"]
            return sub if isinstance(sub, str) else sub.get("id")
        return None
