from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import datetime
from typing import Any

from .enums import (
    Currency,
    LinkStatus,
    LinkType,
    TransactionKind,
    TransactionStatus,
    TransactionType,
)


def _parse_dt(value: str | None) -> datetime | None:
    if not value or not value.strip():
        return None
    v = value.strip()
    if v.endswith("Z"):
        v = v[:-1] + "+00:00"
    if "." in v:
        date_part, rest = v.split(".", 1)
        tz_index = max(rest.find("+"), rest.find("-"))
        if tz_index != -1:
            fraction = rest[:tz_index]
            tz_part = rest[tz_index:]
        else:
            fraction = rest
            tz_part = ""
        if len(fraction) > 6:
            v = f"{date_part}.{fraction[:6]}{tz_part}"
    return datetime.fromisoformat(v)


# ---------------------------------------------------------------------------
# Payment Link
# ---------------------------------------------------------------------------


@dataclass(frozen=True)
class PaymentLink:
    """A payment link issued by WATA.

    Represents a single- or multi-use URL that a customer follows to
    complete a payment.  Full details (redirect URLs, expiry, etc.) are
    available when fetching a link by ID; search results contain a subset
    of fields.
    """

    id: str
    type: LinkType
    amount: float
    currency: Currency
    status: LinkStatus
    url: str = ""
    terminal_name: str = ""
    terminal_public_id: str = ""
    creation_time: datetime | None = None
    order_id: str | None = None
    description: str | None = None
    success_redirect_url: str | None = None
    fail_redirect_url: str | None = None
    expiration_date_time: datetime | None = None
    is_arbitrary_amount_allowed: bool | None = None
    arbitrary_amount_prompts: list[float] | None = None

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> PaymentLink:
        """Construct a :class:`PaymentLink` from a raw API response dict."""
        return cls(
            id=data["id"],
            type=LinkType(data["type"]),
            amount=float(data["amount"]),
            currency=Currency(data["currency"]),
            status=LinkStatus(data["status"]),
            url=data.get("url", ""),
            terminal_name=data.get("terminalName", ""),
            terminal_public_id=data.get("terminalPublicId", ""),
            creation_time=_parse_dt(data.get("creationTime")),
            order_id=data.get("orderId"),
            description=data.get("description"),
            success_redirect_url=data.get("successRedirectUrl"),
            fail_redirect_url=data.get("failRedirectUrl"),
            expiration_date_time=_parse_dt(data.get("expirationDateTime")),
            is_arbitrary_amount_allowed=data.get("isArbitraryAmountAllowed"),
            arbitrary_amount_prompts=data.get("arbitraryAmountPrompts"),
        )


@dataclass(frozen=True)
class PaymentLinkList:
    """Paginated result from a payment link search."""

    items: list[PaymentLink]
    total_count: int

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> PaymentLinkList:
        return cls(
            items=[PaymentLink.from_dict(i) for i in data.get("items", [])],
            total_count=data.get("totalCount", 0),
        )


# ---------------------------------------------------------------------------
# Transaction
# ---------------------------------------------------------------------------


@dataclass(frozen=True)
class Transaction:
    """A payment or refund transaction recorded by WATA.

    A transaction is created when the customer submits the payment form.
    """

    id: str
    type: TransactionType
    amount: float
    currency: Currency
    status: TransactionStatus
    terminal_name: str = ""
    terminal_public_id: str = ""
    creation_time: datetime | None = None
    error_code: str | None = None
    error_description: str | None = None
    order_id: str | None = None
    order_description: str | None = None
    payment_time: datetime | None = None
    total_commission: float | None = None
    sbp_link: str | None = None
    payment_link_id: str | None = None

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> Transaction:
        """Construct a :class:`Transaction` from a raw API response dict."""
        return cls(
            id=data["id"],
            type=TransactionType(data["type"]),
            amount=float(data["amount"]),
            currency=Currency(data["currency"]),
            status=TransactionStatus(data["status"]),
            terminal_name=data.get("terminalName", ""),
            terminal_public_id=data.get("terminalPublicId", ""),
            creation_time=_parse_dt(data.get("creationTime")),
            error_code=data.get("errorCode"),
            error_description=data.get("errorDescription"),
            order_id=data.get("orderId"),
            order_description=data.get("orderDescription"),
            payment_time=_parse_dt(data.get("paymentTime")),
            total_commission=data.get("totalCommission"),
            sbp_link=data.get("sbpLink"),
            payment_link_id=data.get("paymentLinkId"),
        )


@dataclass(frozen=True)
class TransactionList:
    """Paginated result from a transaction search."""

    items: list[Transaction]
    total_count: int

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> TransactionList:
        return cls(
            items=[Transaction.from_dict(i) for i in data.get("items", [])],
            total_count=data.get("totalCount", 0),
        )


# ---------------------------------------------------------------------------
# Refund
# ---------------------------------------------------------------------------


@dataclass(frozen=True)
class RefundResult:
    """Response returned after initiating a refund.

    The final status (``Paid`` or ``Declined``) may arrive asynchronously
    via a webhook.
    """

    transaction_id: str
    transaction_status: TransactionStatus
    kind: TransactionKind
    error_code: str | None = None
    error_description: str | None = None

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> RefundResult:
        return cls(
            transaction_id=data["transactionId"],
            transaction_status=TransactionStatus(data["transactionStatus"]),
            kind=TransactionKind(data["kind"]),
            error_code=data.get("errorCode"),
            error_description=data.get("errorDescription"),
        )


# ---------------------------------------------------------------------------
# Webhook
# ---------------------------------------------------------------------------


@dataclass(frozen=True)
class WebhookPayload:
    """Parsed body of a WATA webhook notification.

    WATA sends webhooks for payment and refund events.  Always verify the
    ``X-Signature`` header with :func:`aiowata.verify_webhook_signature`
    before trusting the payload.
    """

    transaction_type: TransactionType
    kind: TransactionKind
    id: str
    """Payment link ID (not the transaction ID)."""
    transaction_id: str
    terminal_public_id: str
    transaction_status: TransactionStatus
    terminal_name: str
    amount: float
    currency: Currency
    error_code: str | None = None
    error_description: str | None = None
    order_id: str | None = None
    order_description: str | None = None
    commission: float | None = None
    payment_time: datetime | None = None
    email: str | None = None
    payment_link_id: str | None = None

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> WebhookPayload:
        """Construct from a parsed JSON dict."""
        return cls(
            transaction_type=TransactionType(data["transactionType"]),
            kind=TransactionKind(data["kind"]),
            id=data["id"],
            transaction_id=data["transactionId"],
            terminal_public_id=data["terminalPublicId"],
            transaction_status=TransactionStatus(data["transactionStatus"]),
            terminal_name=data.get("terminalName", ""),
            amount=float(data["amount"]),
            currency=Currency(data["currency"]),
            error_code=data.get("errorCode"),
            error_description=data.get("errorDescription"),
            order_id=data.get("orderId"),
            order_description=data.get("orderDescription"),
            commission=data.get("commission"),
            payment_time=_parse_dt(data.get("paymentTime")),
            email=data.get("email"),
            payment_link_id=data.get("paymentLinkId"),
        )

    @classmethod
    def from_raw(cls, body: bytes | str) -> WebhookPayload:
        """Deserialize a raw JSON webhook body.

        :param body: The raw HTTP request body (bytes or string).
        """
        if isinstance(body, bytes):
            body = body.decode("utf-8")
        return cls.from_dict(json.loads(body))
