#!/usr/bin/env python3
"""
PolyTrue MM — Automated market maker for Turbo.

Strategy:
  - Fetches live BTC price from the backend (Pyth-sourced)
  - Calculates YES probability based on price vs strike
  - Quotes a fixed spread around the midpoint
  - Auto-refreshes quotes every 2 seconds
  - Detects fills and tracks P&L
  - Auto-rotates when market changes

Usage:
  python3 mm/polytruemm.py --wallet 0xYOUR_MM_WALLET
  python3 mm/polytruemm.py --wallet 0xYOUR_MM_WALLET --spread 8 --size 5

Controls:
  Ctrl+C to stop (cancels all orders on exit)
"""

import argparse
import math
import signal
import sys
import time

sys.path.insert(0, sys.path[0])
from turbo_client import TurboClient

PRICE_SCALE = 1_000_000


def calc_probability(btc_price: float, strike: float, seconds_remaining: int) -> float:
    """
    Calculate YES probability using normal CDF approximation.
    Based on deviation from strike relative to expected volatility.
    """
    if seconds_remaining <= 0 or strike <= 0:
        return 0.5

    # BTC daily vol ~3%, scale to remaining time
    daily_vol = 0.03
    seconds_in_day = 86400
    vol = daily_vol * math.sqrt(seconds_remaining / seconds_in_day)

    if vol < 0.0001:
        return 0.99 if btc_price >= strike else 0.01

    deviation = (btc_price - strike) / strike
    z = deviation / vol

    # Fast normal CDF approximation
    t = 1.0 / (1.0 + 0.2316419 * abs(z))
    d = 0.3989422804 * math.exp(-z * z / 2)
    p = 1.0 - d * t * (0.3193815 + t * (-0.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))))
    if z < 0:
        p = 1.0 - p

    return max(0.02, min(0.98, p))


def main():
    parser = argparse.ArgumentParser(description="PolyTrue MM for Turbo")
    parser.add_argument("--wallet", required=True, help="MM wallet address")
    parser.add_argument("--key", required=True, help="MM wallet private key (for EIP-712 signing)")
    parser.add_argument("--spread", type=int, default=6, help="Full spread in cents (default: 6)")
    parser.add_argument("--size", type=float, default=1.0, help="Size per side in USDC (default: 1)")
    parser.add_argument("--url", default="http://localhost:8080", help="Backend URL")
    args = parser.parse_args()

    client = TurboClient(args.wallet, args.url, private_key=args.key)
    half_spread = args.spread / 2 / 100  # e.g. 6c spread → 0.03
    size_raw = int(args.size * PRICE_SCALE)

    # Track state
    current_market_id = ""
    bid_order_id = None  # BUY YES
    ask_order_id = None  # SELL YES
    last_mid = 0.0
    cycles = 0
    total_pnl = 0.0

    def cleanup(*_):
        print("\n[MM] Shutting down — cancelling all orders...")
        try:
            n = client.cancel_all()
            print(f"[MM] Cancelled {n} orders")
        except Exception as e:
            print(f"[MM] Cancel error: {e}")
        sys.exit(0)

    signal.signal(signal.SIGINT, cleanup)

    print(f"[MM] PolyTrue MM started")
    print(f"[MM] Wallet:  {args.wallet}")
    print(f"[MM] Spread:  {args.spread}c")
    print(f"[MM] Size:    ${args.size}/side")
    print(f"[MM] Backend: {args.url}")
    print()

    while True:
        try:
            # Get market state
            mkt = client.get_market()
            market_id = mkt.get("marketId", "")
            btc_price = mkt.get("btcPrice", 0)
            strike = mkt.get("strikePrice", 0)
            secs = mkt.get("secondsRemaining", 0)

            if not market_id or btc_price <= 0:
                time.sleep(2)
                continue

            # Market rotation
            if market_id != current_market_id:
                if current_market_id:
                    print(f"\n[MM] Market rotated → {market_id[:16]}...")
                    client.cancel_all()
                    bid_order_id = None
                    ask_order_id = None
                current_market_id = market_id
                print(f"[MM] Market: {market_id[:16]}... | Strike: ${strike:,.2f}")

            # Don't quote in final 15 seconds
            if secs < 15:
                if bid_order_id or ask_order_id:
                    print(f"[MM] Final {secs}s — pulling quotes")
                    client.cancel_all()
                    bid_order_id = None
                    ask_order_id = None
                time.sleep(1)
                continue

            # Calculate fair value
            mid = calc_probability(btc_price, strike, secs)

            # Check if quotes need updating (>1c move)
            if abs(mid - last_mid) < 0.01 and bid_order_id and ask_order_id:
                # Check for fills
                orders = client.get_my_orders()
                order_ids = {o["id"] for o in orders}
                bid_filled = bid_order_id not in order_ids
                ask_filled = ask_order_id not in order_ids

                if bid_filled and ask_filled:
                    cycles += 1
                    pnl = args.spread / 100  # earned the spread
                    total_pnl += pnl
                    print(f"[MM] CYCLE #{cycles} complete! +${pnl:.2f} | Total P&L: ${total_pnl:.2f}")
                    bid_order_id = None
                    ask_order_id = None
                elif bid_filled:
                    print(f"[MM] BID FILLED (bought YES @ {int((mid - half_spread) * 100)}c)")
                    bid_order_id = None
                elif ask_filled:
                    print(f"[MM] ASK FILLED (sold YES @ {int((mid + half_spread) * 100)}c)")
                    ask_order_id = None

                if bid_order_id and ask_order_id:
                    time.sleep(2)
                    continue

            last_mid = mid
            bid_price = int(max(0.01, mid - half_spread) * PRICE_SCALE)
            ask_price = int(min(0.99, mid + half_spread) * PRICE_SCALE)

            # Cancel old orders
            if bid_order_id:
                try:
                    client.cancel_order(bid_order_id)
                except Exception:
                    pass
                bid_order_id = None

            if ask_order_id:
                try:
                    client.cancel_order(ask_order_id)
                except Exception:
                    pass
                ask_order_id = None

            # Place new quotes
            # BUY YES (bid)
            result = client.place_order(
                side=0, outcome=0,
                price=bid_price, size=size_raw,
            )
            if result.get("remaining", 0) > 0:
                bid_order_id = result["orderId"]

            # SELL YES (ask)
            result = client.place_order(
                side=1, outcome=0,
                price=ask_price, size=size_raw,
            )
            if result.get("remaining", 0) > 0:
                ask_order_id = result["orderId"]

            mid_pct = int(mid * 100)
            bid_c = int(bid_price / PRICE_SCALE * 100)
            ask_c = int(ask_price / PRICE_SCALE * 100)
            print(
                f"[MM] BTC ${btc_price:,.0f} | mid {mid_pct}% | "
                f"bid {bid_c}c / ask {ask_c}c | {secs}s left"
            )

        except KeyboardInterrupt:
            cleanup()
        except Exception as e:
            print(f"[MM] Error: {e}")

        time.sleep(2)


if __name__ == "__main__":
    main()
