"""
QuickBooks Online webhook payload schemas.

These schemas validate incoming webhook notifications from QBO.
QBO sends data change notifications when entities are created,
updated, deleted, merged, or voided.

QBO Webhook Documentation:
https://developer.intuit.com/app/developer/qbo/docs/develop/webhooks

Usage:
    from lightwave.schema.pydantic.contracts.webhooks.quickbooks import QBOWebhookPayload

    @router.post("/webhooks/qbo/")
    def handle_qbo_webhook(request):
        # Validate signature first (see QBO docs)
        payload = QBOWebhookPayload.model_validate_json(request.body)

        for notification in payload.event_notifications:
            realm_id = notification.realm_id
            for entity in notification.data_change_event.entities:
                process_entity_change(
                    realm_id=realm_id,
                    entity_type=entity.name,
                    entity_id=entity.id,
                    operation=entity.operation,
                )
"""

from datetime import datetime
from typing import Literal

from pydantic import Field

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

# =============================================================================
# QBO Entity Types
# =============================================================================

QBOEntityName = Literal[
    "Account",
    "Bill",
    "BillPayment",
    "Budget",
    "Class",
    "CreditMemo",
    "Customer",
    "Department",
    "Deposit",
    "Employee",
    "Estimate",
    "Invoice",
    "Item",
    "JournalEntry",
    "Payment",
    "PaymentMethod",
    "Preferences",
    "Purchase",
    "PurchaseOrder",
    "RefundReceipt",
    "SalesReceipt",
    "TaxAgency",
    "TaxCode",
    "TaxRate",
    "Term",
    "TimeActivity",
    "Transfer",
    "Vendor",
    "VendorCredit",
]

QBOOperation = Literal[
    "Create",
    "Update",
    "Delete",
    "Merge",
    "Void",
    "Emailed",
]


# =============================================================================
# Webhook Payload Schemas
# =============================================================================


class QBOEntityChange(LightwaveBaseSchema):
    """
    A single entity change notification from QBO.

    Represents one entity that was created, updated, deleted, etc.

    Example:
        {
            "name": "Customer",
            "id": "123",
            "operation": "Update",
            "lastUpdated": "2024-01-20T10:30:00.000Z"
        }
    """

    name: QBOEntityName = Field(..., description="Entity type name")
    id: str = Field(..., description="Entity ID in QBO")
    operation: QBOOperation = Field(..., description="Operation performed")
    last_updated: datetime | None = Field(
        None,
        alias="lastUpdated",
        description="When entity was last updated",
    )


class QBODataChangeEvent(LightwaveBaseSchema):
    """
    Data change event containing multiple entity changes.

    Example:
        {
            "entities": [
                {"name": "Customer", "id": "123", "operation": "Update"},
                {"name": "Invoice", "id": "456", "operation": "Create"}
            ]
        }
    """

    entities: list[QBOEntityChange] = Field(
        default_factory=list,
        description="List of changed entities",
    )


class QBOEventNotification(LightwaveBaseSchema):
    """
    Event notification for a specific QBO realm (company).

    Each notification is scoped to a single realm (QBO company).

    Example:
        {
            "realmId": "123456789",
            "dataChangeEvent": {
                "entities": [...]
            }
        }
    """

    realm_id: str = Field(
        ...,
        alias="realmId",
        description="QBO company ID (realm)",
    )
    data_change_event: QBODataChangeEvent = Field(
        ...,
        alias="dataChangeEvent",
        description="Data change event payload",
    )


class QBOWebhookPayload(LightwaveBaseSchema):
    """
    Root webhook payload from QuickBooks Online.

    This is the top-level schema for all QBO webhook notifications.
    A single payload can contain notifications for multiple realms.

    Example payload:
        {
            "eventNotifications": [
                {
                    "realmId": "123456789",
                    "dataChangeEvent": {
                        "entities": [
                            {"name": "Customer", "id": "123", "operation": "Create"},
                            {"name": "Invoice", "id": "456", "operation": "Update"}
                        ]
                    }
                }
            ]
        }

    Usage:
        payload = QBOWebhookPayload.model_validate_json(request.body)
        for notification in payload.event_notifications:
            realm_id = notification.realm_id
            for entity in notification.data_change_event.entities:
                # Process each entity change
                ...
    """

    event_notifications: list[QBOEventNotification] = Field(
        ...,
        alias="eventNotifications",
        description="List of event notifications",
    )

    def get_changes_by_realm(self) -> dict[str, list[QBOEntityChange]]:
        """
        Group all entity changes by realm ID.

        Returns:
            Dict mapping realm_id to list of entity changes
        """
        changes: dict[str, list[QBOEntityChange]] = {}
        for notification in self.event_notifications:
            realm_id = notification.realm_id
            if realm_id not in changes:
                changes[realm_id] = []
            changes[realm_id].extend(notification.data_change_event.entities)
        return changes

    def get_changes_by_entity_type(self, entity_type: QBOEntityName) -> list[tuple[str, QBOEntityChange]]:
        """
        Get all changes for a specific entity type.

        Args:
            entity_type: Entity type to filter (e.g., "Customer", "Invoice")

        Returns:
            List of (realm_id, entity_change) tuples
        """
        result: list[tuple[str, QBOEntityChange]] = []
        for notification in self.event_notifications:
            for entity in notification.data_change_event.entities:
                if entity.name == entity_type:
                    result.append((notification.realm_id, entity))
        return result
