from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Dict, Optional


@dataclass
class ProviderConfig:
    provider_slug: str
    credentials: Dict[str, str]
    config_data: Dict[str, Any] = field(default_factory=dict)


@dataclass
class IframeConfig:
    sdk_url: str
    api_key: str
    merchant_id: str
    mode: str
    extra: Dict[str, Any] = field(default_factory=dict)


@dataclass
class TokenDetails:
    provider_payment_method_id: str
    provider: str
    brand: Optional[str] = None
    last4: Optional[str] = None
    exp_month: Optional[str] = None
    exp_year: Optional[str] = None
    account_type: Optional[str] = None
    bank_name: Optional[str] = None
    reference_token: Optional[str] = None


@dataclass
class ChargeResult:
    transaction_id: str
    status: str  # "succeeded" | "failed" | "processing"
    amount: float
    currency: str
    raw_response: Dict[str, Any]


class BasePaymentProvider(ABC):
    @property
    @abstractmethod
    def slug(self) -> str: ...

    @abstractmethod
    def get_iframe_config(self, config: ProviderConfig, amount: float = 0.0, currency: str = "USD") -> IframeConfig: ...

    @abstractmethod
    async def get_token_details(self, config: ProviderConfig, token: str) -> TokenDetails: ...

    @abstractmethod
    async def create_payment_method(
        self, config: ProviderConfig, token: str, customer_context: Dict[str, Any]
    ) -> TokenDetails: ...

    @abstractmethod
    async def submit_charge(
        self,
        config: ProviderConfig,
        amount: float,
        currency: str,
        payment_method_token: str,
        payment_method_type: str,
        capture: bool,
        idempotency_key: Optional[str],
        metadata: Optional[Dict[str, Any]],
    ) -> ChargeResult: ...

    @abstractmethod
    async def refund_charge(
        self,
        config: ProviderConfig,
        provider_transaction_id: str,
        amount: float,
        reason: Optional[str],
    ) -> ChargeResult: ...

    @abstractmethod
    def verify_webhook(
        self, config: ProviderConfig, headers: Dict[str, str], raw_body: bytes
    ) -> bool: ...

    @abstractmethod
    def parse_webhook_event(self, payload: Dict[str, Any]) -> Dict[str, Any]: ...
