import httpx
import csv
import os
import time
from datetime import datetime
from pytz import timezone
from collections import defaultdict

# ----------------------------
# CONFIG
# ----------------------------

BASE_URL = "http://localhost:25503/v3"
SYMBOL = "SPXW"
SNAPSHOT_STRIKES = 120
TIMEZONE = timezone("US/Eastern")
SAVE_FOLDER = "./snapshots"
CSV_COLS = [
    "timestamp", "underlying_price", "strike",
    "bid_call", "ask_call", "bid_put", "ask_put",
    "delta_call", "delta_put",
    "gamma_call", "open_interest_call", "volume_call",
    "gamma_put", "open_interest_put", "volume_put"
]

# ----------------------------
# FETCH FUNCTIONS
# ----------------------------

def fetch_csv(endpoint, params):
    try:
        r = httpx.get(f"{BASE_URL}/{endpoint}", params=params, timeout=10)
        r.raise_for_status()
        return list(csv.reader(r.text.strip().splitlines()))
    except Exception as e:
        print(f"[ERROR] {endpoint}: {e}")
        return []

def fetch_snapshot():
    params = {"symbol": SYMBOL, "expiration": datetime.now(TIMEZONE).strftime("%Y-%m-%d"), "format": "csv"}
    quotes = fetch_csv("option/snapshot/quote", params)
    greeks = fetch_csv("option/snapshot/greek", params)
    stats  = fetch_csv("option/snapshot/stat", params)
    under  = fetch_csv("option/snapshot/underlying", {"symbol": SYMBOL, "format": "csv"})
    return quotes, greeks, stats, under

# ----------------------------
# GAMMA CALCULATION
# ----------------------------

def compute_numeric_gamma(greek_rows):
    grouped = defaultdict(list)
    for row in greek_rows:
        try:
            strike = float(row[2])
            right  = row[3].upper()
            delta  = float(row[7])
            grouped[right].append((strike, delta))
        except: continue

    result = {}
    for right, lst in grouped.items():
        lst.sort()
        for i, (k, d) in enumerate(lst):
            if i == 0:
                k2, d2 = lst[i + 1]
                gamma = abs((d2 - d) / (k2 - k))
            elif i == len(lst) - 1:
                k1, d1 = lst[i - 1]
                gamma = abs((d - d1) / (k - k1))
            else:
                k1, d1 = lst[i - 1]
                k2, d2 = lst[i + 1]
                gamma = abs((d2 - d1) / (k2 - k1))
            result[(k, right)] = gamma
    return result

# ----------------------------
# COMBINE AND FILTER
# ----------------------------

def build_snapshot():
    quotes, greeks, stats, under = fetch_snapshot()
    if len(quotes) < 2 or len(greeks) < 2 or len(under) < 1:
        print("[WARN] Datos insuficientes para snapshot.")
        return []

    timestamp = datetime.utcnow().isoformat()
    try:
        under_price = float(under[1][1])  # Assumes price in second row, second column
    except:
        print("[WARN] Precio subyacente no disponible.")
        return []

    greek_map = {(float(r[2]), r[3].upper()): float(r[7]) for r in greeks if r[0] != "symbol"}
    delta_map = {(float(r[2]), r[3].upper()): float(r[7]) for r in greeks if r[0] != "symbol"}
    gamma_map = compute_numeric_gamma(greeks[1:])
    stat_map = {(float(r[2]), r[3].upper()): (int(r[5]), int(r[6])) for r in stats if r[0] != "symbol"}

    combined = {}
    for r in quotes:
        if r[0] == "symbol": continue
        strike = float(r[2])
        right  = r[3].upper()
        key = (strike, right)
        if key not in combined:
            combined[key] = {
                "timestamp": timestamp,
                "underlying_price": under_price,
                "strike": strike,
                "bid_call": "", "ask_call": "", "bid_put": "", "ask_put": "",
                "delta_call": "", "delta_put": "",
                "gamma_call": "", "open_interest_call": "", "volume_call": "",
                "gamma_put": "", "open_interest_put": "", "volume_put": ""
            }

        if right == "CALL":
            combined[key]["bid_call"] = r[7]
            combined[key]["ask_call"] = r[11]
            combined[key]["delta_call"] = delta_map.get(key, "")
            combined[key]["gamma_call"] = gamma_map.get(key, "")
            oi, vol = stat_map.get(key, ("", ""))
            combined[key]["open_interest_call"] = oi
            combined[key]["volume_call"] = vol
        elif right == "PUT":
            combined[key]["bid_put"] = r[7]
            combined[key]["ask_put"] = r[11]
            combined[key]["delta_put"] = delta_map.get(key, "")
            combined[key]["gamma_put"] = gamma_map.get(key, "")
            oi, vol = stat_map.get(key, ("", ""))
            combined[key]["open_interest_put"] = oi
            combined[key]["volume_put"] = vol

    # seleccionar los N strikes más cercanos al ATM por delta
    rows = list(combined.values())
    rows.sort(key=lambda r: min(abs(float(r["delta_call"] or 99) - 0.5), abs(float(r["delta_put"] or -99) + 0.5)))
    return rows[:SNAPSHOT_STRIKES]

# ----------------------------
# SAVE CSV
# ----------------------------

def save_snapshot(rows):
    if not rows:
        return
    today_str = datetime.now(TIMEZONE).strftime("%Y-%m-%d")
    os.makedirs(SAVE_FOLDER, exist_ok=True)
    filename = os.path.join(SAVE_FOLDER, f"chain_intraday_{SYMBOL}_{today_str}.csv")
    file_exists = os.path.isfile(filename)

    with open(filename, "a", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=CSV_COLS)
        if not file_exists:
            writer.writeheader()
        for row in rows:
            writer.writerow(row)

# ----------------------------
# STREAM LOOP
# ----------------------------

def run():
    print("=== STREAMING SNAPSHOT SPXW ===")
    while True:
        rows = build_snapshot()
        save_snapshot(rows)
        time.sleep(10)

if __name__ == "__main__":
    run()