"""Natural Payments SDK models."""

from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import Literal

from pydantic import BaseModel, Field, model_validator


class TransactionTypeFilter(str, Enum):
    """Filter for transaction types in list operations."""

    PAYMENT = "payment"
    TRANSFER = "transfer"
    ALL = "all"


class Payment(BaseModel):
    """Payment result.

    Handles both /payments/initiate (MCP) and /payments responses.
    """

    # Core ID - accepts both 'transfer_id' and 'id' from different endpoints
    transfer_id: str = Field(default="")
    payment_id: str | None = None

    # Status values from natural-api payment orchestration
    # PENDING_CLAIM: Payment sent to provisional recipient (needs to claim)
    # COMPLETED: Payment completed successfully
    # PENDING: Payment in progress
    # FAILED: Payment failed
    status: str  # Allow any status string from server

    amount: float | str | None = None  # v1 returns string, initiate returns float
    currency: str = "USD"
    recipient_email: str | None = None
    recipient_phone: str | None = None
    memo: str | None = None  # Also mapped from 'description'
    created_at: datetime | str | None = None  # Server may return ISO string
    updated_at: datetime | str | None = None  # v1 endpoint includes this
    claim_link: str | None = None
    counterparty_party_id: str | None = None
    customer_party_id: str | None = None  # v1 endpoint includes this
    delegation_id: str | None = None  # v1 endpoint includes this
    instance_id: str | None = None  # Echoed back for audit grouping

    @model_validator(mode="before")
    @classmethod
    def normalize_fields(cls, data: dict) -> dict:
        """Normalize field names from different endpoint responses."""
        if isinstance(data, dict):
            # Map 'id' to 'transfer_id' if transfer_id not present
            if "id" in data and "transfer_id" not in data:
                data["transfer_id"] = data["id"]
            # Map 'description' to 'memo' if memo not present
            if "description" in data and "memo" not in data:
                data["memo"] = data["description"]
        return data


class AmountInfo(BaseModel):
    """Amount with minor and dollar representations."""

    amount_minor: int
    amount_dollars: str


class BalanceBreakdown(BaseModel):
    """Detailed balance breakdown."""

    operating_funded: AmountInfo
    operating_advanced: AmountInfo
    escrow_funded_settled: AmountInfo
    escrow_advanced: AmountInfo
    holds_outbound: AmountInfo


class AssetBalance(BaseModel):
    """Balance for a specific asset/currency."""

    asset_code: str
    breakdown: BalanceBreakdown
    available: AmountInfo
    metadata: dict | None = None


class AccountBalance(BaseModel):
    """Wallet balance information from /api/wallet/balance."""

    wallet_id: str
    balances: list[AssetBalance]

    @property
    def available_usd(self) -> float:
        """Get available USD balance as a float."""
        for balance in self.balances:
            if balance.asset_code == "USD":
                return float(balance.available.amount_dollars)
        return 0.0


class Transaction(BaseModel):
    """Transaction record.

    Handles various response formats from the transactions API.
    """

    transaction_id: str = Field(default="")
    type: str | None = None  # payment_sent, payment_received, deposit, withdrawal, or None
    status: str  # Pending, Processing, Completed, Failed (case-insensitive)
    amount: float | str  # May come as string or float
    currency: str = "USD"
    counterparty: str | None = None
    counterparty_party_id: str | None = None
    memo: str | None = None
    description: str | None = None  # Alternative to memo
    created_at: datetime | str | None = None
    completed_at: datetime | str | None = None
    updated_at: datetime | str | None = None

    @model_validator(mode="before")
    @classmethod
    def normalize_fields(cls, data: dict) -> dict:
        """Normalize field names from API response."""
        if isinstance(data, dict):
            # Map 'id' to 'transaction_id' if transaction_id not present
            if "id" in data and "transaction_id" not in data:
                data["transaction_id"] = data["id"]
            # Normalize status to lowercase for consistency
            if "status" in data and isinstance(data["status"], str):
                data["status"] = data["status"].lower()
        return data


class CancellationResult(BaseModel):
    """Payment cancellation result."""

    status: str
    message: str


class ParsedIntent(BaseModel):
    """Parsed payment intent from natural language."""

    class Recipient(BaseModel):
        email: str | None = None
        phone: str | None = None
        bank_token: str | None = None
        counterparty_id: str | None = None

    class BusinessContext(BaseModel):
        memo: str | None = None

    recipient: Recipient
    amount: float | None = None
    currency: str = "USD"
    business_context: BusinessContext | None = None


# =============================================================================
# AGENT MODELS
# =============================================================================


class Agent(BaseModel):
    """Agent entity."""

    agent_id: str
    id: str  # Same as agent_id (BFF returns both)
    name: str
    description: str | None = None
    status: Literal["ACTIVE", "REVOKED"]
    party_id: str
    created_at: datetime | str
    created_by: str
    updated_at: datetime | str
    updated_by: str


class AgentCreateResponse(BaseModel):
    """Response from agent creation."""

    agent_id: str
    name: str
    description: str | None = None
    status: Literal["ACTIVE", "REVOKED"]
    party_id: str
    created_at: datetime | str
    created_by: str


class AgentUpdateResponse(BaseModel):
    """Response from agent update."""

    agent_id: str
    name: str
    description: str | None = None
    status: Literal["ACTIVE", "REVOKED"]
    party_id: str
    updated_at: datetime | str
    updated_by: str


class AgentListResponse(BaseModel):
    """Response for list agents endpoint."""

    agents: list[Agent]
    has_more: bool
    next_cursor: str | None = None


# =============================================================================
# DELEGATION MODELS
# =============================================================================


class Delegation(BaseModel):
    """Delegation entity (party-to-party trust relationship)."""

    id: str
    handle: str
    delegating_party_id: str  # Customer party granting access
    delegated_party_id: str  # Partner party receiving access
    permissions: list[str]
    expires_at: datetime | str | None = None
    status: str  # ACTIVE, SUSPENDED, REVOKED
    created_at: datetime | str
    created_by: str | None = None


class DelegationListResponse(BaseModel):
    """Response for list delegations endpoint."""

    delegations: list[Delegation]
    total: int


# =============================================================================
# CUSTOMER MODELS
# =============================================================================


class CustomerPartyInfo(BaseModel):
    """Customer party information."""

    id: str
    handle: str | None = None
    type: str  # PERSON or ORG
    legal_name: str | None = None
    display_name: str | None = None
    status: str | None = None


class Customer(BaseModel):
    """Customer with delegation details."""

    party: CustomerPartyInfo
    delegation_id: str
    permissions: list[str] = Field(default_factory=list)
    delegation_status: str
    created_at: str


# =============================================================================
# DEPOSIT/WITHDRAW MODELS
# =============================================================================


class DepositResponse(BaseModel):
    """Response from deposit initiation."""

    transfer_id: str | None = None
    status: str  # PENDING, PROCESSING, COMPLETED, FAILED
    amount: float | str
    currency: str = "USD"
    estimated_settlement: datetime | str | None = None
    error: str | None = None
    error_details: str | None = None


class WithdrawResponse(BaseModel):
    """Response from withdrawal initiation."""

    transfer_id: str | None = None
    status: str  # PROCESSING, KYC_REQUIRED, MFA_REQUIRED, FAILED
    amount: float | str
    currency: str = "USD"
    estimated_settlement: datetime | str | None = None
    # KYC fields
    kyc_required: bool = False
    kyc_status: str | None = None
    kyc_session_url: str | None = None
    # MFA fields
    mfa_required: bool = False
    mfa_challenge_id: str | None = None
    mfa_expires_at: datetime | str | None = None
    # Error fields
    error: str | None = None
    error_details: str | None = None
