import json
import time
import re
import streamlit as st
import requests
import numpy as np
import pandas as pd
from datetime import datetime
from config import SESSION, FIDELITY, PRIVATE_KEY, WALLET_ADDRESS
from py_clob_client.client import ClobClient
from py_clob_client.order_builder.constants import BUY, SELL
from py_clob_client.clob_types import MarketOrderArgs

STANDARD_BINS = [f"{i*20}-{i*20+19}" for i in range(30)]

def parse_lower(label: str) -> int:
    s = str(label).lower().strip()
    if any(k in s for k in ['+', 'more', 'over', 'greater']):
        m = re.search(r'(\d+)', s)
        return int(m.group(1)) if m else 99999
    m = re.search(r'(\d+)[^\d]+(\d+)', s)
    if m:
        return int(m.group(1))
    m = re.search(r'(\d+)', s)
    return int(m.group(1)) if m else 0

def get_event_title(meta, filename):
    title = meta.get("event_title") or meta.get("title") or meta.get("name") or ""
    if isinstance(title, np.ndarray):
        title = str(title.item() if title.size == 1 else title)
    return str(title)

def get_category_from_event_title(event_title):
    event_title = event_title.lower()
    if re.search(r'(march|april|may|june|july|august|september|october|november|december|january|february)-\d{4}', event_title):
        return "month"
    year_match = re.search(r'20(\d{2})', event_title)
    year = int("20" + year_match.group(1)) if year_match else 2026
    match = re.search(r'([a-z]+)\s*(\d+)\s*-\s*([a-z]+)\s*(\d+)', event_title)
    if match:
        s_month, s_day, e_month, e_day = match.groups()
        month_map = {"january":1,"february":2,"march":3,"april":4,"may":5,"june":6,"july":7,"august":8,"september":9,"october":10,"november":11,"december":12}
        start = datetime(year, month_map.get(s_month, 1), int(s_day))
        end = datetime(year, month_map.get(e_month, 1), int(e_day))
        if end < start:
            end = end.replace(year=end.year + 1)
        duration = (end - start).days + 1
        weekday = start.weekday()
        if duration <= 3:
            return "2day"
        if weekday == 1:
            return "7day_tue"
        if weekday == 4:
            return "7day_fri"
        return "other"
    return "other"
def discover_active_slugs():
    slugs = set()
    try:
        r = SESSION.get("https://polymarket.com/predictions/elon-tweets", timeout=25)
        found = re.findall(r'(elon-musk-of-tweets-[a-z]*-[\d]*-[a-z]*-[\d]*)', r.text)
        for s in found:
            if len(s) < 80 and not re.search(r'\.(jpg|png|gif)', s):
                slugs.add(s)
    except:
        pass
    for sl in slugs:
        fetch_new_prices(sl)
    return list(dict.fromkeys(slugs))

def fetch_event(slug):
    """Ultra-robust fetch that never returns None for real Elon events"""
    #print(f"[FETCH_EVENT] {datetime.now().strftime('%H:%M:%S')} — Trying slug '{slug}'")
    urls = [
        f"https://gamma-api.polymarket.com/events?slug={slug}",
        f"https://gamma-api.polymarket.com/markets?slug={slug}",
        f"https://gamma-api.polymarket.com/events?slug={slug}&closed=false",
        f"https://gamma-api.polymarket.com/events?slug={slug}&closed=true"
    ]
    for url in urls:
        try:
            r = SESSION.get(url, timeout=15)
            #print(f"[FETCH_EVENT]   {url} → status {r.status_code}")
            if r.status_code != 200:
                continue
            raw = r.json()
            if isinstance(raw, list) and raw:
                #print(f"[FETCH_EVENT]   ✅ Found event (list)")
                return raw[0]
            if isinstance(raw, dict):
                for key in ["data", "events", "markets", "items", "event"]:
                    if raw.get(key):
                        v = raw[key]
                        if isinstance(v, list) and v:
                            #print(f"[FETCH_EVENT]   ✅ Found event (dict/{key})")
                            return v[0]
                        if isinstance(v, dict):
                            #print(f"[FETCH_EVENT]   ✅ Found event (dict/{key})")
                            return v
            print(f"[FETCH_EVENT]   No usable data in response")
        except Exception as e:
            print(f"[FETCH_EVENT]   Error on {url}: {e}")
    print(f"[FETCH_EVENT] ❌ All attempts failed for {slug}")
    return None
def extract_yes_buckets(event):
    outcomes = event.get("outcomes")
    if isinstance(outcomes, str):
        try: outcomes = json.loads(outcomes)
        except: outcomes = []
    clobs = event.get("clobTokenIds")
    if isinstance(clobs, str):
        try: clobs = json.loads(clobs)
        except: clobs = []
    if outcomes and clobs and len(outcomes) == len(clobs) and len(outcomes) >= 8:
        return [{"label": str(label).strip(), "token": clobs[i]} for i, label in enumerate(outcomes)]

    markets = event.get("markets", []) or [event]
    buckets = []
    for m in markets:
        q = m.get("question", "")
        if "elon" not in q.lower() or "tweet" not in q.lower():
            continue
        clobs = m.get("clobTokenIds")
        if isinstance(clobs, str):
            try: clobs = json.loads(clobs)
            except: continue
        if clobs:
            buckets.append({"label": q.strip(), "token": clobs[0]})
    seen = {b["label"]: b for b in buckets}
    return sorted(seen.values(), key=lambda x: x["label"])
def fetch_new_prices(slug):
    try:
        resp = requests.get(f"https://gamma-api.polymarket.com/events?slug={slug}")
        event = resp.json()[0] if isinstance(resp.json(), list) else resp.json()
        markets = event["markets"]
        buckets = extract_yes_buckets(event)
        tokens = [b['token'] for b in buckets]
        all_timestamps = set()
        price_dict = {}
        for token in tokens:
            if not token:
                continue
            r = requests.get(
                    f"https://clob.polymarket.com/prices-history?market={token}&startTs={int(time.mktime(datetime.fromisoformat(event["startDate"].split("T")[0]).timetuple()))}&endTs={int(time.time())}&fidelity=30"
            )
            history = r.json()
            if not history:
                continue
            times = [p["t"] for p in history["history"]]
            prices = [p["p"] for p in history["history"]]
            all_timestamps.update(times)
            price_dict[token] = dict(zip(times, prices))

        if not all_timestamps:
            st.error("No data received from API")
        else:
            full_ts = sorted(all_timestamps)
            full_matrix = []
            for token in tokens:
                token_id = token
                if token_id not in price_dict:
                    full_matrix.append([0] * len(full_ts))
                    continue
                row = [price_dict[token_id].get(t, np.nan) for t in full_ts]
                full_matrix.append(row)

            full_matrix = np.array(full_matrix).T
            df = pd.DataFrame(full_matrix)
            df = df.ffill().bfill().fillna(0)
            full_matrix = df.values

            out_file = f"./data/elon_surface_{slug}_FULL.npz"

            meta = {
                "event_slug": slug,
                "event_title": event.get("title") or event.get("question", slug),
                "start_date": event["startDate"],
                "end_date": event["endDate"],
                "bins": [b["label"] for b in buckets],
                "timestamps": full_ts,
                "fidelity_min": FIDELITY,
                "num_bins": len(buckets)
            }
            np.savez_compressed(out_file, matrix=full_matrix, **meta)
            #print(f"   ✅ SAVED — {len(full_ts)} ts × {len(buckets)} bins")
    except Exception as e:
        st.error(f"API call failed: {e}")
        st.info("Use the 'Show Exact Curl Commands' button above and run them in your terminal.")
        return False

def parse_bucket_range(q: str) -> tuple[float, float] | None:
    q = q.lower()
    m = re.search(r"(\d+)(?:\s*or more\+|)", q)
    if m: return float(m.group(1)), float(m.group(1)) + 20.0
    m = re.search(r"(\d+)[^\d]+(\d+)", q)
    if m: return float(m.group(1)), float(m.group(2))
    m = re.search(r"(?:less than|<)\s*(\d+)", q)
    if m: return 0.0, float(m.group(1))
    return None

def midpoint(low: float, high: float) -> float:
    """Clean midpoint — works perfectly with the new normalized buckets"""
    return (low + high) / 2
# ====================== CONSTANTS & HELPERS ======================ENDS_HERE the logic inside this block MUST BE KEPT AS THEY ARE!!


def get_orderbook_data(token: str, side: str = None):
    """Pure official CLOB /book endpoint with EXPLICIT price sorting.
    
    Terminology (you had it right):
      - Bid  = highest price someone is willing to PAY → you can SELL at the highest bid
      - Ask  = lowest price someone is willing to SELL at → you can BUY at the lowest ask
    
    For rules:
      - BUY  → lowest Ask (price you pay)
      - SELL → highest Bid (price you receive)
    """
    if not token:
        return 0.0005, 0, 0.0

    url = f"https://clob.polymarket.com/book?token_id={token}"
    try:
        r = SESSION.get(url, timeout=10)
        r.raise_for_status()
        data = r.json()

        bids = data.get("bids", [])
        asks = data.get("asks", [])

        # === EXPLICIT SORTING (no more [0] assumption) ===
        # bids: descending price (highest bid first)
        bids = sorted(bids, key=lambda x: float(x["price"]), reverse=True)
        # asks: ascending price (lowest ask first)
        asks = sorted(asks, key=lambda x: float(x["price"]))

        if bids and asks:
            best_bid = float(bids[0]["price"])
            best_ask = float(asks[0]["price"])

            if side == "SELL":
                execution_price = best_bid      # highest bid
            elif side == "BUY":
                execution_price = best_ask      # lowest ask
            else:
                execution_price = (best_bid + best_ask) / 2

            # Volume and vola
            bid_vol = sum(float(b["size"]) for b in bids[:5])
            ask_vol = sum(float(a["size"]) for a in asks[:5])
            volume = int((bid_vol + ask_vol) * 100)
            vola = abs(best_ask - best_bid)

            #print(f"[ORDERBOOK] {token[:12]}... side={side or 'mid'} → Price={execution_price:.4f} (bid={best_bid:.4f}, ask={best_ask:.4f}) Vol={volume} Vola={vola:.4f}")
            return execution_price, volume, vola

        #print(f"[ORDERBOOK] {token[:12]}... → empty book → fallback 0.0005")
        return 0.0005, 0, 0.0
    except Exception as e:
        print(f"[ORDERBOOK] Error for {token[:12]}: {e}")
        return 0.0005, 0, 0.0
