"""
SwarmPay Client – Der Haupteinstiegspunkt für Entwickler.
3 Zeilen Code um AI-Agent Payments zu aktivieren.

Usage:
    from swarmpay import SwarmPay

    sp = SwarmPay(cdp_api_key_id="...", cdp_api_key_secret="...")
    wallet = sp.create_wallet("my-agent")
    result = wallet.pay("0xRecipient", amount=0.50)
"""
from __future__ import annotations
import os
import time
from swarmpay.wallet import AgentWallet
from swarmpay.fee_engine import FeeEngine, FeeConfig
from swarmpay.registry import ServiceRegistry, ServiceListing
from swarmpay.escrow import EscrowEngine, EscrowConfig
from swarmpay.reputation import ReputationEngine, ReputationConfig
from swarmpay.permissions import PermissionEngine
from swarmpay.onchain_escrow import (
    OnchainEscrowActionResult,
    OnchainEscrowClient,
    OnchainEscrowConfig,
    OnchainEscrowFlowResult,
    OnchainEscrowIntent,
    OnchainEscrowStatus,
    create_onchain_record,
    build_escrow_id,
    hash_escrow_metadata,
)
from swarmpay.wallet import _to_atomic_units
from swarmpay.constants import TOKEN_DECIMALS
from swarmpay.webhooks import WebhookDispatcher


class SwarmPay:
    """
    SwarmPay – The Payment Layer for AI Agents.

    Args:
        cdp_api_key_id: Coinbase Developer Platform API Key ID
        cdp_api_key_secret: CDP API Key Secret
        admin_wallet: Deine Wallet für Fee-Einnahmen
        fee_percent: Gebühr pro Transaktion (default: 1%)
        network: Blockchain-Netzwerk (default: base-sepolia Testnet)
        escrow_config: Escrow configuration (None = no escrow)
        reputation_config: Reputation configuration (None = no reputation)
        permission_engine: Permission engine instance (None = no limits)
    """

    def __init__(
        self,
        cdp_api_key_id: str = "",
        cdp_api_key_secret: str = "",
        admin_wallet: str = "",
        fee_percent: float = 0.01,
        network: str = "base-sepolia",
        escrow_config: EscrowConfig | None = None,
        reputation_config: ReputationConfig | None = None,
        permission_engine: PermissionEngine | None = None,
        onchain_escrow_config: OnchainEscrowConfig | None = None,
        webhook_dispatcher: WebhookDispatcher | None = None,
    ):
        self.network = network
        self._cdp_key_id = cdp_api_key_id or os.getenv("CDP_API_KEY_ID", "")
        self._cdp_key_secret = cdp_api_key_secret or os.getenv("CDP_API_KEY_SECRET", "")
        self._cdp_wallet_secret = os.getenv("CDP_WALLET_SECRET", "")
        self._admin_wallet = (
            admin_wallet
            or os.getenv("SWARMPAY_ADMIN_WALLET", "")
            or os.getenv("PAYFI_ADMIN_WALLET", "")
        )

        # Fee Engine
        self._fee_engine = FeeEngine(FeeConfig(
            base_fee_percent=fee_percent,
            admin_wallet=self._admin_wallet,
        ))

        # Service Registry
        self._registry = ServiceRegistry()

        # Escrow Engine (optional)
        self._escrow_engine = EscrowEngine(escrow_config) if escrow_config else None

        # Reputation Engine (optional)
        self._reputation_engine = ReputationEngine(reputation_config) if reputation_config else None

        # Permission Engine (optional)
        self._permission_engine = permission_engine
        self._onchain_escrow = (
            OnchainEscrowClient(onchain_escrow_config)
            if onchain_escrow_config else None
        )
        self._webhook_dispatcher = webhook_dispatcher or WebhookDispatcher()

        # Wallet-Provider (Coinbase AgentKit)
        self._wallet_provider = None
        if self._cdp_key_id and self._cdp_key_secret:
            self._init_coinbase()

        # Erstellte Wallets tracken
        self._wallets: dict[str, AgentWallet] = {}

    def _init_coinbase(self):
        """Initialisiert Coinbase AgentKit als Wallet-Provider."""
        try:
            from coinbase_agentkit import (
                CdpEvmWalletProvider,
                CdpEvmWalletProviderConfig,
            )
            self._wallet_provider = CdpEvmWalletProvider(
                CdpEvmWalletProviderConfig(
                    api_key_id=self._cdp_key_id,
                    api_key_secret=self._cdp_key_secret,
                    wallet_secret=self._cdp_wallet_secret,
                    network_id=self.network,
                )
            )
            print(f"[SwarmPay] Coinbase AgentKit verbunden ({self.network})")
        except ImportError:
            print("[SwarmPay] coinbase-agentkit nicht installiert.")
            print("[SwarmPay] Installiere mit: pip install coinbase-agentkit")
            print("[SwarmPay] Starte im Demo-Modus (ohne echte Blockchain).")
        except Exception as e:
            print(f"[SwarmPay] Coinbase-Verbindung fehlgeschlagen: {e}")
            print("[SwarmPay] Prüfe deine CDP API Keys.")

    def create_wallet(self, agent_id: str) -> AgentWallet:
        """Erstellt eine neue Wallet für einen KI-Agenten."""
        wallet = AgentWallet(
            agent_id=agent_id,
            wallet_provider=self._wallet_provider,
            fee_engine=self._fee_engine,
            network=self.network,
            permission_engine=self._permission_engine,
        )
        self._wallets[agent_id] = wallet
        print(f"[SwarmPay] Wallet erstellt für Agent '{agent_id}': {wallet.address}")
        return wallet

    def register_service(self, wallet: AgentWallet, service_type: str,
                         price_usd: float, description: str = "",
                         endpoint: str = "") -> str:
        """Registriert einen Service in der Registry."""
        listing = ServiceListing(
            wallet=wallet.address,
            service_type=service_type,
            price_usd=price_usd,
            description=description,
            endpoint=endpoint,
        )
        service_id = self._registry.register(listing)
        print(f"[SwarmPay] Service registriert: {service_type} @ ${price_usd}")
        return service_id

    def find_service(self, service_type: str) -> list[dict]:
        """Findet verfügbare Services eines Typs."""
        return self._registry.find(service_type)

    def find_cheapest(self, service_type: str) -> dict | None:
        """Findet den besten Anbieter (Preis + Reputation wenn aktiviert)."""
        if self._reputation_engine:
            providers = self._registry.find(service_type)
            return self._reputation_engine.find_best(providers)
        return self._registry.find_cheapest(service_type)

    def buy_service(self, buyer_wallet: AgentWallet,
                    service_type: str) -> dict:
        """
        Kauft den besten verfügbaren Service (direkte Zahlung).
        Für Escrow-geschützte Zahlung: buy_service_escrow()
        """
        provider = self.find_cheapest(service_type)
        if not provider:
            return {"error": f"Kein Anbieter für '{service_type}' gefunden."}

        result = buyer_wallet.pay(
            recipient=provider["wallet"],
            amount=provider["price_usd"],
        )

        # Reputation tracken wenn aktiviert
        if self._reputation_engine and result.success:
            self._reputation_engine.record_success(provider["wallet"])

        return {
            "payment": result,
            "provider": provider,
        }

    @property
    def onchain_escrow(self) -> OnchainEscrowClient | None:
        """Access the configured on-chain escrow client, if enabled."""
        return self._onchain_escrow

    @property
    def webhook_dispatcher(self) -> WebhookDispatcher:
        """Access the in-process webhook/event dispatcher."""
        return self._webhook_dispatcher

    def create_onchain_escrow(
        self,
        buyer_wallet: AgentWallet,
        provider_wallet: str,
        amount_usd: float,
        service_type: str,
        escrow_nonce: int,
        timeout_seconds: int = 3600,
        reference: str = "",
        salt: str = "",
    ) -> OnchainEscrowFlowResult:
        """Fund a new on-chain escrow directly from amount and service metadata."""
        if not self._onchain_escrow:
            raise ValueError("On-chain escrow not enabled. Pass onchain_escrow_config to SwarmPay().")
        if amount_usd <= 0:
            raise ValueError("Escrow amount must be greater than 0")

        fee_info = self._fee_engine.calculate_fee(amount_usd, buyer_wallet.address)
        decimals = TOKEN_DECIMALS.get("usdc", 6)
        total_amount_atomic = _to_atomic_units(amount_usd, decimals)
        provider_amount_atomic = _to_atomic_units(fee_info["service_amount"], decimals)
        fee_amount_atomic = total_amount_atomic - provider_amount_atomic
        deadline_unix = int(time.time()) + timeout_seconds
        metadata_hash = hash_escrow_metadata(service_type, reference, salt)

        intent = OnchainEscrowIntent(
            buyer_wallet=buyer_wallet.address,
            provider_wallet=provider_wallet,
            token_address=self._onchain_escrow.config.token_address,
            total_amount_atomic=total_amount_atomic,
            provider_amount_atomic=provider_amount_atomic,
            fee_amount_atomic=fee_amount_atomic,
            deadline_unix=deadline_unix,
            metadata_hash=metadata_hash,
        )
        funding = self._onchain_escrow.fund_with_wallet(
            wallet=buyer_wallet,
            escrow_nonce=escrow_nonce,
            intent=intent,
        )
        record = create_onchain_record(
            self._onchain_escrow.config,
            escrow_nonce=escrow_nonce,
            intent=intent,
            status=OnchainEscrowStatus.FUNDED,
            tx_hash=funding.funding_tx_hash,
        )
        self._webhook_dispatcher.emit_and_deliver(
            "escrow.funded",
            {
                "escrow_id": record.escrow_id,
                "chain_id": record.chain_id,
                "contract_address": record.contract_address,
                "escrow_nonce": record.escrow_nonce,
                "buyer_wallet": record.buyer_wallet,
                "provider_wallet": record.provider_wallet,
                "service_type": service_type,
                "amount_atomic": record.total_amount_atomic,
                "provider_amount_atomic": record.provider_amount_atomic,
                "fee_amount_atomic": record.fee_amount_atomic,
                "funding_tx_hash": funding.funding_tx_hash,
                "approval_tx_hash": funding.approval_tx_hash,
                "status": record.status.value,
            },
        )
        return OnchainEscrowFlowResult(
            escrow=record,
            approval_tx_hash=funding.approval_tx_hash,
            funding_tx_hash=funding.funding_tx_hash,
            provider=None,
        )

    def buy_service_onchain_escrow(
        self,
        buyer_wallet: AgentWallet,
        service_type: str,
        escrow_nonce: int,
        timeout_seconds: int = 3600,
        salt: str = "",
    ) -> dict:
        """Find a provider and fund an on-chain escrow for that service."""
        provider = self.find_cheapest(service_type)
        if not provider:
            return {"error": f"Kein Anbieter für '{service_type}' gefunden."}

        flow = self.create_onchain_escrow(
            buyer_wallet=buyer_wallet,
            provider_wallet=provider["wallet"],
            amount_usd=provider["price_usd"],
            service_type=service_type,
            escrow_nonce=escrow_nonce,
            timeout_seconds=timeout_seconds,
            reference=provider.get("service_type", service_type),
            salt=salt,
        )
        return {"escrow": flow.escrow, "provider": provider, "transactions": flow}

    def _require_onchain_escrow(self) -> OnchainEscrowClient:
        if not self._onchain_escrow:
            raise ValueError("On-chain escrow not enabled. Pass onchain_escrow_config to SwarmPay().")
        return self._onchain_escrow

    def _resolve_onchain_escrow_id(self, escrow_nonce: int, escrow_id: str = "") -> str:
        if escrow_id:
            return escrow_id
        client = self._require_onchain_escrow()
        return build_escrow_id(
            client.config.chain_id,
            client.config.contract_address,
            escrow_nonce,
        )

    def confirm_onchain_delivery(
        self,
        wallet: AgentWallet,
        escrow_nonce: int,
        escrow_id: str = "",
    ) -> OnchainEscrowActionResult:
        client = self._require_onchain_escrow()
        tx_hash = client.confirm_delivery_with_wallet(wallet, escrow_nonce)
        resolved_escrow_id = self._resolve_onchain_escrow_id(escrow_nonce, escrow_id)
        self._webhook_dispatcher.emit_and_deliver(
            "escrow.delivered",
            {
                "escrow_id": resolved_escrow_id,
                "escrow_nonce": escrow_nonce,
                "actor_wallet": wallet.address,
                "tx_hash": tx_hash,
                "status": OnchainEscrowStatus.DELIVERED.value,
            },
        )
        return OnchainEscrowActionResult(
            escrow_id=resolved_escrow_id,
            escrow_nonce=escrow_nonce,
            status=OnchainEscrowStatus.DELIVERED,
            tx_hash=str(tx_hash),
        )

    def release_onchain_escrow(
        self,
        wallet: AgentWallet,
        escrow_nonce: int,
        escrow_id: str = "",
    ) -> OnchainEscrowActionResult:
        client = self._require_onchain_escrow()
        tx_hash = client.release_with_wallet(wallet, escrow_nonce)
        resolved_escrow_id = self._resolve_onchain_escrow_id(escrow_nonce, escrow_id)
        self._webhook_dispatcher.emit_and_deliver(
            "escrow.released",
            {
                "escrow_id": resolved_escrow_id,
                "escrow_nonce": escrow_nonce,
                "actor_wallet": wallet.address,
                "tx_hash": tx_hash,
                "status": OnchainEscrowStatus.RELEASED.value,
            },
        )
        return OnchainEscrowActionResult(
            escrow_id=resolved_escrow_id,
            escrow_nonce=escrow_nonce,
            status=OnchainEscrowStatus.RELEASED,
            tx_hash=str(tx_hash),
        )

    def refund_onchain_escrow(
        self,
        wallet: AgentWallet,
        escrow_nonce: int,
        escrow_id: str = "",
    ) -> OnchainEscrowActionResult:
        client = self._require_onchain_escrow()
        tx_hash = client.refund_with_wallet(wallet, escrow_nonce)
        resolved_escrow_id = self._resolve_onchain_escrow_id(escrow_nonce, escrow_id)
        self._webhook_dispatcher.emit_and_deliver(
            "escrow.refunded",
            {
                "escrow_id": resolved_escrow_id,
                "escrow_nonce": escrow_nonce,
                "actor_wallet": wallet.address,
                "tx_hash": tx_hash,
                "status": OnchainEscrowStatus.REFUNDED.value,
            },
        )
        return OnchainEscrowActionResult(
            escrow_id=resolved_escrow_id,
            escrow_nonce=escrow_nonce,
            status=OnchainEscrowStatus.REFUNDED,
            tx_hash=str(tx_hash),
        )

    def dispute_onchain_escrow(
        self,
        wallet: AgentWallet,
        escrow_nonce: int,
        reason: str = "",
        escrow_id: str = "",
    ) -> OnchainEscrowActionResult:
        client = self._require_onchain_escrow()
        reason_hash = hash_escrow_metadata("dispute", reason)
        tx_hash = client.dispute_with_wallet(wallet, escrow_nonce, reason_hash)
        resolved_escrow_id = self._resolve_onchain_escrow_id(escrow_nonce, escrow_id)
        self._webhook_dispatcher.emit_and_deliver(
            "escrow.disputed",
            {
                "escrow_id": resolved_escrow_id,
                "escrow_nonce": escrow_nonce,
                "actor_wallet": wallet.address,
                "tx_hash": tx_hash,
                "status": OnchainEscrowStatus.DISPUTED.value,
                "reason": reason,
                "reason_hash": reason_hash,
            },
        )
        return OnchainEscrowActionResult(
            escrow_id=resolved_escrow_id,
            escrow_nonce=escrow_nonce,
            status=OnchainEscrowStatus.DISPUTED,
            tx_hash=str(tx_hash),
            reason_hash=reason_hash,
        )

    # ── Escrow Methods ────────────────────────────────────────

    def buy_service_escrow(self, buyer_wallet: AgentWallet,
                           service_type: str,
                           timeout_seconds: int | None = None) -> dict:
        """
        Kauft einen Service mit Escrow-Schutz.
        Geld wird erst nach Delivery-Bestätigung freigegeben.
        """
        if not self._escrow_engine:
            return {"error": "Escrow not enabled. Pass escrow_config to SwarmPay()."}

        provider = self.find_cheapest(service_type)
        if not provider:
            return {"error": f"Kein Anbieter für '{service_type}' gefunden."}

        # Fee berechnen
        fee_info = self._fee_engine.calculate_fee(
            provider["price_usd"], buyer_wallet.address
        )

        # Escrow erstellen (Geld wird gehalten)
        escrow = self._escrow_engine.create_escrow(
            buyer_wallet=buyer_wallet.address,
            provider_wallet=provider["wallet"],
            amount_usd=provider["price_usd"],
            fee_usd=fee_info["fee_amount"],
            service_amount_usd=fee_info["service_amount"],
            service_type=service_type,
            timeout_seconds=timeout_seconds,
        )

        return {
            "escrow": escrow,
            "provider": provider,
            "status": escrow.state,
        }

    def confirm_delivery(self, escrow_id: str, provider_wallet: str) -> dict:
        """Provider bestätigt Service-Delivery."""
        if not self._escrow_engine:
            return {"error": "Escrow not enabled."}
        escrow = self._escrow_engine.confirm_delivery(escrow_id, provider_wallet)
        return {"escrow": escrow, "status": escrow.state}

    def release_escrow(self, escrow_id: str) -> dict:
        """Gibt Escrow-Geld an Provider frei."""
        if not self._escrow_engine:
            return {"error": "Escrow not enabled."}
        escrow = self._escrow_engine.release(escrow_id)

        # Reputation: Success
        if self._reputation_engine:
            response_time = escrow.released_at - escrow.created_at
            self._reputation_engine.record_success(
                escrow.provider_wallet, response_time
            )

        return {"escrow": escrow, "status": escrow.state}

    def dispute_escrow(self, escrow_id: str, buyer_wallet: str,
                       reason: str = "") -> dict:
        """Buyer beanstandet die Lieferung."""
        if not self._escrow_engine:
            return {"error": "Escrow not enabled."}
        escrow = self._escrow_engine.dispute(escrow_id, buyer_wallet, reason)

        # Reputation: Dispute
        if self._reputation_engine:
            self._reputation_engine.record_dispute(escrow.provider_wallet)

        return {"escrow": escrow, "status": escrow.state}

    def refund_escrow(self, escrow_id: str) -> dict:
        """Erstattet Escrow-Geld an Buyer."""
        if not self._escrow_engine:
            return {"error": "Escrow not enabled."}
        escrow = self._escrow_engine.refund(escrow_id)

        # Reputation: Failure
        if self._reputation_engine:
            self._reputation_engine.record_failure(escrow.provider_wallet)

        return {"escrow": escrow, "status": escrow.state}

    def get_escrow(self, escrow_id: str):
        """Escrow-Status abfragen."""
        if not self._escrow_engine:
            return None
        return self._escrow_engine.get_escrow(escrow_id)

    # ── Reputation Methods ────────────────────────────────────

    def get_reputation(self, wallet: str):
        """Agent-Reputation abfragen."""
        if not self._reputation_engine:
            return None
        return self._reputation_engine.get_reputation(wallet)

    # ── Properties ────────────────────────────────────────────

    @property
    def fee_schedule(self) -> list[dict]:
        """Aktuelle Gebührenstruktur."""
        return self._fee_engine.get_fee_schedule()

    @property
    def registry(self) -> ServiceRegistry:
        """Zugriff auf die Service-Registry."""
        return self._registry

    @property
    def escrow_engine(self):
        return self._escrow_engine

    @property
    def reputation_engine(self):
        return self._reputation_engine

    @property
    def permission_engine(self):
        return self._permission_engine
