"""Payment providers are the interfaces and implementations for payment methods."""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_payment_providers.ipynb.

# %% auto 0
__all__ = ['PaymentProvider', 'CoinbaseProvider', 'PaymentRequest', 'get_payment_request', 'Client']

# %% ../nbs/01_payment_providers.ipynb 2
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Optional, TypedDict

from fastcore.utils import *
import fastcore.basics as fc
import os
import json
from .core import *
from .utils import *
import httpx
from pydantic import BaseModel


# %% ../nbs/01_payment_providers.ipynb 3
class PaymentProvider(ABC):
    """Base class for all payment providers"""
    supported_methods: list[str] = []  # Will be overridden by each provider
    
    @abstractmethod
    def pay(self, *args, **kwargs):
        pass


# %% ../nbs/01_payment_providers.ipynb 5
from cdp import *

class CoinbaseProvider(fc.BasicRepr):
    def __init__(self, wallet: Wallet,
                asset: str = 'usdc'):
        store_attr()
        self.supported_methods=["onchain"]
        self.chain = self.wallet.network_id

    # NOTE: in the fewsats example, we will return an async taskID
    def pay(self,
            amount: float,
            address: str,
            asset: str):
        # TODO, return a generic async task 
        return self.wallet.transfer(amount, asset, address).wait()

# %% ../nbs/01_payment_providers.ipynb 7
class PaymentRequest(BaseModel):
    """Represents a payment request to get payment details from a payment provider"""
    offer_id: str
    payment_context_token: str
    payment_method: str
    chain: str = ""
    asset: str = ""

# class PaymentStatus:
#     PENDING = "pending"
#     COMPLETED = "completed"
#     FAILED = "failed"
#     EXPIRED = "expired"


# %% ../nbs/01_payment_providers.ipynb 10
def get_payment_request(payment_request_url: str,
                        payment_context_token: str,
                        offer_id: str, 
                        payment_method: str, 
                        chain: str = "", 
                        asset: str = ""):
    data = {
        "offer_id": offer_id,
        "payment_method": payment_method,
        "chain": chain,
        "asset": asset,
        "payment_context_token": payment_context_token
    }
    r = httpx.post(payment_request_url, json=data)
    r.raise_for_status()
    return r.json()


# %% ../nbs/01_payment_providers.ipynb 12
class Client(fc.BasicRepr):
    def __init__(self, lightning_provider = None, 
                 credit_card_provider = None, 
                 onchain_provider = None):
        store_attr()
        self.lightning_provider = lightning_provider
        self.credit_card_provider = credit_card_provider
        self.onchain_provider = onchain_provider


    def pay(self, ofr: dict): # ofr is the l402 offers response dictionary
        "Pay for an offer"
        # this actually does 3 things
        # 1. Selects offer
        # 2. Gets payment request details
        # 3. Uses user-provided payment method
        
        ofr = L402Response(**ofr)
        if len(ofr.offers) != 1: raise ValueError("Only one offer is supported")
        o = first(ofr.offers)

        if 'onchain' in o.payment_methods and self.onchain_provider:
            r = get_payment_request(ofr.payment_request_url, ofr.payment_context_token, o.offer_id, 'onchain', self.onchain_provider.chain, self.onchain_provider.asset)

            return self.onchain_provider.pay(o.amount, r['payment_request']['address'], r['payment_request']['asset'])
            
        # elif 'lightning' in o.payment_methods and self.lightning_provider:
        # elif 'credit_card' in o.payment_methods and self.credit_card_provider:
        else:
            raise ValueError(f"No payment provider available for {ofr.offers[0].payment_methods}")

