"""
Polymarket Adapter
==================
BrokerAdapter implementation for Polymarket prediction markets.

All operations are proxied through the polymarket-direct Supabase Edge Function
which handles wallet credential decryption and CLOB API auth server-side.
"""

from __future__ import annotations

import os
import logging
from datetime import datetime
from typing import AsyncIterator, Dict, Any, Iterable, List, Optional

import requests

from ..enums import OrderSide, OrderType, TimeInForce
from ..interfaces import BrokerAdapter
from ..models import (
    OrderSubmitRequest,
    OrderReplaceRequest,
    Order,
    Account,
    Position,
    Quote,
    Bar,
)

logger = logging.getLogger(__name__)

SUPABASE_URL = "https://brkcjojfmlygsujiqglv.supabase.co"
SUPABASE_ANON_KEY = (
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
    "eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJya2Nqb2pmbWx5Z3N1amlxZ2x2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDc5MDg3NTksImV4cCI6MjA2MzQ4NDc1OX0."
    "1mM0avWJHVH-7_MKIW7_gvh-WwMj97qTVOe0pbk0S-E"
)


class PolymarketAdapter(BrokerAdapter):
    """
    Polymarket adapter for CPZ Python SDK.

    Connects to the Polymarket prediction market through the
    ``polymarket-direct`` Supabase Edge Function. The edge function
    handles wallet credential decryption and CLOB API authentication.

    Prerequisites:
        1. Connect your Polygon wallet via the CPZAI web UI
           (Settings -> Connect Broker -> Polymarket).
        2. The connected wallet will appear in your trading credentials.

    Usage::

        from cpz import CPZClient

        client = CPZClient()
        client.execution.use_broker("polymarket")

        account = client.execution.get_account()
        positions = client.execution.get_positions()

        order = client.execution.submit_order(
            OrderSubmitRequest(
                symbol="market-condition-id",
                side=OrderSide.BUY,
                qty=100,
                type=OrderType.LIMIT,
                limit_price=0.65,
                outcome="YES",
            )
        )
    """

    def __init__(
        self,
        cpz_api_key: str,
        account_id: str,
        supabase_url: str = SUPABASE_URL,
        supabase_anon_key: str = SUPABASE_ANON_KEY,
        timeout: int = 30,
    ) -> None:
        self._cpz_api_key = cpz_api_key
        self._account_id = account_id
        self._supabase_url = supabase_url.rstrip("/")
        self._anon_key = supabase_anon_key
        self._timeout = timeout
        self._fn_url = f"{self._supabase_url}/functions/v1/polymarket-direct"

    @staticmethod
    def create(**kwargs: object) -> "PolymarketAdapter":
        from ...common.cpz_ai import CPZAIClient

        account_id = str(kwargs.get("account_id") or "")
        env = str(kwargs.get("env") or os.getenv("POLYMARKET_ENV", "live"))
        supabase_url = str(kwargs.get("supabase_url") or os.getenv("SUPABASE_URL", SUPABASE_URL))
        supabase_anon_key = str(
            kwargs.get("supabase_anon_key")
            or os.getenv("SUPABASE_ANON_KEY", SUPABASE_ANON_KEY)
        )

        platform = CPZAIClient.from_env()
        cpz_api_key = platform.api_key

        if not account_id:
            try:
                creds = platform.get_broker_credentials(
                    broker="polymarket", env=env or None, account_id=None
                )
                if creds:
                    account_id = str(creds.get("id") or creds.get("account_id") or "")
            except Exception as exc:
                logger.warning("Failed to get Polymarket credentials from platform: %s", exc)

        if not account_id:
            raise ValueError(
                "Polymarket wallet not found. Connect your Polygon wallet via the CPZAI "
                "web UI first (Settings -> Connect Broker -> Polymarket)."
            )

        return PolymarketAdapter(
            cpz_api_key=cpz_api_key,
            account_id=account_id,
            supabase_url=supabase_url,
            supabase_anon_key=supabase_anon_key,
        )

    def _call(self, action: str, extra: Optional[Dict[str, Any]] = None) -> Any:
        """Call the polymarket-direct edge function."""
        payload: Dict[str, Any] = {"action": action, "accountId": self._account_id}
        if extra:
            payload.update(extra)

        headers = {
            "apikey": self._anon_key,
            "X-CPZ-Key": self._cpz_api_key,
            "Content-Type": "application/json",
        }

        try:
            resp = requests.post(
                self._fn_url, json=payload, headers=headers, timeout=self._timeout
            )
        except requests.exceptions.Timeout:
            raise Exception(f"Polymarket edge function timeout after {self._timeout}s")
        except requests.exceptions.ConnectionError:
            raise Exception(f"Failed to connect to Polymarket edge function at {self._fn_url}")

        if resp.status_code >= 400:
            error_text = resp.text
            try:
                error_data = resp.json()
                error_text = error_data.get("message") or error_data.get("error") or error_text
            except Exception:
                pass
            raise Exception(f"Polymarket error ({resp.status_code}): {error_text}")

        data = resp.json()
        if data.get("error"):
            raise Exception(data.get("message") or data["error"])
        return data.get("data", data)

    # BrokerAdapter implementation

    def get_account(self) -> Account:
        data = self._call("account")
        return Account(
            id=str(data.get("wallet_address") or data.get("account_id") or self._account_id),
            buying_power=float(data.get("buying_power") or 0),
            equity=float(data.get("equity") or data.get("portfolio_value") or 0),
            cash=float(data.get("cash") or data.get("buying_power") or 0),
        )

    def get_positions(self) -> list[Position]:
        data = self._call("positions")
        if not isinstance(data, list):
            data = []
        return [
            Position(
                symbol=str(p.get("symbol", "")),
                qty=float(p.get("qty") or 0),
                avg_entry_price=float(p.get("avg_entry_price") or 0),
                market_value=float(p.get("market_value") or 0),
            )
            for p in data
        ]

    def get_orders(self, status: Optional[str] = None, limit: int = 100) -> list[Order]:
        data = self._call("orders")
        if not isinstance(data, list):
            data = []

        side_map = {"buy": OrderSide.BUY, "sell": OrderSide.SELL}
        type_map = {"market": OrderType.MARKET, "limit": OrderType.LIMIT}

        orders = []
        for o in data:
            order_status = str(o.get("status", "unknown"))
            if status and order_status != status:
                continue
            raw_side = str(o.get("side", "buy")).lower()
            raw_type = str(o.get("type", "limit")).lower()
            orders.append(
                Order(
                    id=str(o.get("id", "")),
                    symbol=str(o.get("symbol", "")),
                    side=side_map.get(raw_side, OrderSide.BUY),
                    qty=float(o.get("qty") or 0),
                    filled_qty=float(o.get("filled_qty") or 0),
                    type=type_map.get(raw_type, OrderType.LIMIT),
                    time_in_force=TimeInForce.GTC,
                    status=order_status,
                    created_at=datetime.utcnow(),
                )
            )
        return orders[:limit]

    def submit_order(self, request: OrderSubmitRequest) -> Order:
        order_payload: Dict[str, Any] = {
            "token_id": getattr(request, "token_id", None) or request.symbol,
            "polymarket_token_id": getattr(request, "token_id", None) or request.symbol,
            "side": request.side.value if hasattr(request.side, "value") else str(request.side),
            "qty": request.qty,
            "type": request.type.value if hasattr(request.type, "value") else str(request.type),
            "outcome": getattr(request, "outcome", "YES"),
        }
        if request.limit_price is not None:
            order_payload["limit_price"] = request.limit_price

        result = self._call("place_order", {"order": order_payload})
        return Order(
            id=str(result.get("id") or result.get("orderID") or ""),
            symbol=request.symbol,
            side=request.side,
            qty=request.qty or 0,
            filled_qty=0,
            type=request.order_type,
            time_in_force=request.time_in_force,
            status=str(result.get("status", "new")),
            created_at=datetime.utcnow(),
        )

    def cancel_order(self, order_id: str) -> None:
        self._call("cancel_order", {"orderId": order_id})

    def replace_order(self, order_id: str, request: OrderReplaceRequest) -> Order:
        self.cancel_order(order_id)
        submit_req = OrderSubmitRequest(
            symbol=request.symbol or "",
            side=OrderSide.BUY,
            qty=request.qty or 0,
            type=OrderType.LIMIT,
            limit_price=request.limit_price,
        )
        return self.submit_order(submit_req)

    def get_order(self, order_id: str) -> Order:
        orders = self.get_orders()
        for o in orders:
            if o.id == order_id:
                return o
        raise ValueError(f"Order {order_id} not found")

    def cancel_all_orders(self) -> int:
        orders = self.get_orders(status="new")
        for o in orders:
            try:
                self.cancel_order(o.id)
            except Exception:
                pass
        return len(orders)

    def get_quote(self, symbol: str) -> Quote:
        raise NotImplementedError("Use client.data.polymarket for market quotes")

    def get_bars(self, symbol: str, timeframe: str = "1Day", limit: int = 100) -> list[Bar]:
        raise NotImplementedError("Use client.data.polymarket for market data")

    async def stream_quotes(self, symbols: Iterable[str]) -> AsyncIterator[Quote]:
        raise NotImplementedError("Polymarket streaming not yet supported")

    async def stream_bars(self, symbols: Iterable[str], timeframe: str = "1Min") -> AsyncIterator[Bar]:
        raise NotImplementedError("Polymarket streaming not yet supported")
