"""
PolyEdge — Polymarket API Wrapper

Connects to Polymarket's CLOB API for live trading.
Docs: https://docs.polymarket.com/developers/CLOB/introduction

This is the integration layer — everything else (signals, risk) stays the same.
For live deployment, you need:
1. A Polymarket account with USDC deposited
2. An API key from the CLOB
3. A private key for signing orders (EIP-712)
"""

import json
import time
import requests
from typing import Dict, Any, Optional, List
from datetime import datetime


# API Endpoints
GAMMA_API = "https://gamma-api.polymarket.com"
CLOB_API = "https://clob.polymarket.com"
DATA_API = "https://data-api.polymarket.com"


class PolymarketClient:
    """
    Wrapper for Polymarket's APIs.
    
    For backtesting: use mock mode (no API calls)
    For live: set api_key and private_key
    """
    
    def __init__(self, api_key: str = "", private_key: str = "", mock: bool = True):
        self.api_key = api_key
        self.private_key = private_key
        self.mock = mock
        self.session = requests.Session()
        
        if api_key:
            self.session.headers.update({
                "Authorization": f"Bearer {api_key}",
                "Content-Type": "application/json",
            })
    
    # =========================================================================
    # MARKET DISCOVERY (Gamma API)
    # =========================================================================
    
    def get_markets(self, tag: str = "crypto", limit: int = 50) -> List[Dict]:
        """Fetch active crypto markets."""
        if self.mock:
            return [{"id": "mock", "question": "Mock BTC market"}]
        
        resp = self.session.get(
            f"{GAMMA_API}/markets",
            params={"tag": tag, "limit": limit, "active": True, "closed": False}
        )
        resp.raise_for_status()
        return resp.json()
    
    def get_5min_btc_markets(self) -> List[Dict]:
        """Fetch active 5-minute BTC binary markets."""
        markets = self.get_markets(tag="crypto", limit=100)
        # Filter for 5-min BTC markets
        btc_5min = [
            m for m in markets
            if "btc" in m.get("question", "").lower()
            and ("5 min" in m.get("question", "").lower() 
                 or "5min" in m.get("question", "").lower())
        ]
        return btc_5min
    
    # =========================================================================
    # ORDER BOOK (CLOB API)
    # =========================================================================
    
    def get_orderbook(self, token_id: str) -> Dict:
        """Get current order book for a token."""
        if self.mock:
            return {"bids": [], "asks": []}
        
        resp = self.session.get(f"{CLOB_API}/book", params={"token_id": token_id})
        resp.raise_for_status()
        return resp.json()
    
    def get_midpoint(self, token_id: str) -> float:
        """Get midpoint price for a token."""
        if self.mock:
            return 0.50
        
        resp = self.session.get(f"{CLOB_API}/midpoint", params={"token_id": token_id})
        resp.raise_for_status()
        return float(resp.json().get("mid", 0.50))
    
    def get_price(self, token_id: str, side: str = "BUY") -> float:
        """Get best price for a side (BUY/SELL)."""
        if self.mock:
            return 0.50
        
        resp = self.session.get(
            f"{CLOB_API}/price",
            params={"token_id": token_id, "side": side}
        )
        resp.raise_for_status()
        return float(resp.json().get("price", 0.50))
    
    # =========================================================================
    # TRADING (CLOB API)
    # =========================================================================
    
    def place_order(
        self,
        token_id: str,
        side: str,  # "BUY" or "SELL"
        price: float,
        size: float,
        order_type: str = "GTC",  # GTC, FOK, GTD
    ) -> Dict:
        """
        Place a limit order.
        
        Note: In production, this requires EIP-712 signed order messages.
        The actual signing logic depends on the py-clob-client SDK.
        
        For now, this is a placeholder that shows the structure.
        Use: pip install py-clob-client for the real implementation.
        """
        if self.mock:
            return {
                "id": f"mock_{int(time.time())}",
                "status": "LIVE",
                "token_id": token_id,
                "side": side,
                "price": price,
                "size": size,
            }
        
        # In production, use py-clob-client:
        # from py_clob_client.client import ClobClient
        # client = ClobClient(host, key=api_key, chain_id=137, signature_type=2)
        # order = client.create_and_post_order(OrderArgs(...))
        
        raise NotImplementedError(
            "Live trading requires py-clob-client SDK. "
            "Install: pip install py-clob-client\n"
            "See: https://github.com/Polymarket/py-clob-client"
        )
    
    def cancel_order(self, order_id: str) -> Dict:
        """Cancel an open order."""
        if self.mock:
            return {"status": "cancelled"}
        
        resp = self.session.delete(f"{CLOB_API}/order/{order_id}")
        resp.raise_for_status()
        return resp.json()
    
    def get_open_orders(self) -> List[Dict]:
        """Get all open orders."""
        if self.mock:
            return []
        
        resp = self.session.get(f"{CLOB_API}/orders")
        resp.raise_for_status()
        return resp.json()
    
    # =========================================================================
    # ACCOUNT (Data API)
    # =========================================================================
    
    def get_positions(self) -> List[Dict]:
        """Get current positions."""
        if self.mock:
            return []
        
        resp = self.session.get(f"{DATA_API}/positions")
        resp.raise_for_status()
        return resp.json()
    
    def get_balance(self) -> float:
        """Get USDC balance."""
        if self.mock:
            return 1000.0
        
        # This typically requires checking the on-chain balance
        # or using the Data API
        resp = self.session.get(f"{DATA_API}/balance")
        resp.raise_for_status()
        return float(resp.json().get("balance", 0))


def test_connection():
    """Test API connectivity (read-only, no auth needed)."""
    try:
        client = PolymarketClient(mock=False)
        markets = client.get_markets(limit=5)
        print(f"✅ Connected to Polymarket. Found {len(markets)} markets.")
        for m in markets[:3]:
            q = m.get('question', m.get('title', 'Unknown'))
            print(f"  • {q[:60]}")
        return True
    except Exception as e:
        print(f"❌ Connection failed: {e}")
        return False


if __name__ == "__main__":
    test_connection()
