#!/usr/bin/env python3
"""One-shot flatten: cancel_all then IOC market sell reduce-only for BTC long.

Safe for Denis cash / no-hold: reduce_only=True, limited slippage.
"""
from __future__ import annotations

import asyncio
import os
import sys
import time
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))

_env = ROOT / ".env"
if _env.exists():
    for line in _env.read_text().splitlines():
        line = line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        k, _, v = line.partition("=")
        k, v = k.strip(), v.strip().strip("'").strip('"')
        if k and k not in os.environ:
            os.environ[k] = v

from lighter import AccountApi, SignerClient  # noqa: E402
from src.market_data import discover_market  # noqa: E402
from src.socks_proxy import install_socks_proxy  # noqa: E402


def _to_int_amount(value: float, decimals: int) -> int:
    return int(round(float(value) * (10 ** int(decimals))))


async def main() -> int:
    for k in ("LIGHTER_API_PRIVATE_KEY", "LIGHTER_ACCOUNT_INDEX", "LIGHTER_API_KEY_INDEX"):
        if not os.environ.get(k):
            print(f"missing {k}", file=sys.stderr)
            return 2

    if not os.environ.get("LIGHTER_SOCKS_PROXY") and not os.environ.get("LIGHTER_SOCKS"):
        os.environ["LIGHTER_SOCKS"] = "1"
    proxy = install_socks_proxy()
    print(f"socks={proxy}")

    rest = "https://mainnet.zklighter.elliot.ai"
    meta = await discover_market(rest, "BTC", 1)
    print(
        f"market id={meta.market_id} mark={meta.mark_price} "
        f"size_dec={meta.size_decimals} price_dec={meta.price_decimals}"
    )

    acc = int(os.environ["LIGHTER_ACCOUNT_INDEX"])
    idx = int(os.environ["LIGHTER_API_KEY_INDEX"])
    pk = os.environ["LIGHTER_API_PRIVATE_KEY"].strip()
    client = SignerClient(rest, acc, {idx: pk})

    from src.live_engine import _from_str_amount

    async def _read_inv() -> float:
        acct = await AccountApi(client.api_client).account(by="index", value=str(acc))
        accounts = getattr(acct, "accounts", None) or []
        a0 = accounts[0] if accounts else acct
        inv_local = 0.0
        for p in getattr(a0, "positions", None) or []:
            if int(getattr(p, "market_id", -1)) != int(meta.market_id):
                continue
            size = float(_from_str_amount(p.position))
            sign = int(getattr(p, "sign", None) or 1)
            inv_local = size * sign
        return inv_local

    inv = await _read_inv()
    print(f"inv_before={inv:.6f}")
    if abs(inv) < 1e-5:
        print("already flat")
        await client.close()
        return 0
    if inv < 0:
        print("short position — this script only flattens LONG (ask/sell)", file=sys.stderr)
        await client.close()
        return 3

    # Cancel all open orders first so we don't fight resting asks / nonce
    try:
        _, _, err = await client.cancel_all_orders(
            time_in_force=client.CANCEL_ALL_TIF_IMMEDIATE,
            timestamp_ms=0,
            cancel_all_market_index=meta.market_id,
            api_key_index=idx,
        )
        print(f"cancel_all err={err}")
    except Exception as e:
        print(f"cancel_all exception: {e}")

    await asyncio.sleep(0.4)

    size_btc = abs(inv)
    # slight under-size dust to avoid overshoot if account rounds
    size_btc = max(0.0, size_btc - 1e-6)
    base_amount = _to_int_amount(size_btc, meta.size_decimals)
    if base_amount <= 0:
        print("base_amount<=0", file=sys.stderr)
        await client.close()
        return 4

    # Slippage 15 bps for urgency flatten
    max_slip = 0.0015
    coi = int(time.time() * 1000) % 1_000_000_000
    print(f"IOC reduce-only SELL size_btc={size_btc:.6f} base={base_amount} slip={max_slip}")
    tx, resp, err = await client.create_market_order_limited_slippage(
        market_index=meta.market_id,
        client_order_index=coi,
        base_amount=base_amount,
        max_slippage=max_slip,
        is_ask=True,
        reduce_only=True,
        api_key_index=idx,
    )
    print(f"order err={err} resp_code={getattr(resp, 'code', None)} tx={bool(tx)}")
    if err:
        # retry once with wider slip 40 bps
        max_slip = 0.004
        coi2 = coi + 1
        print(f"retry slip={max_slip}")
        tx, resp, err = await client.create_market_order_limited_slippage(
            market_index=meta.market_id,
            client_order_index=coi2,
            base_amount=base_amount,
            max_slippage=max_slip,
            is_ask=True,
            reduce_only=True,
            api_key_index=idx,
        )
        print(f"retry err={err} resp_code={getattr(resp, 'code', None)} tx={bool(tx)}")

    await asyncio.sleep(1.2)
    inv2 = await _read_inv()
    print(f"inv_after={inv2:.6f}")
    await client.close()
    if abs(inv2) > 0.02:
        print("WARN: still meaningful inventory", file=sys.stderr)
        return 5
    return 0 if err is None else 6


if __name__ == "__main__":
    raise SystemExit(asyncio.run(main()))
