"""
saxo_broker.py — Saxo Bank SIM paper trading integration.

Reads SAXO_TOKEN + SAXO_BASE from .env (or environment).
Token expires after 24h — refresh at developer.saxobank.com/openapi/token
"""
import os
import sys
import json
import time
import requests
from pathlib import Path
from datetime import datetime, timezone
from dotenv import load_dotenv
from db import get_conn

load_dotenv()

BASE = os.getenv("SAXO_BASE", "https://gateway.saxobank.com/sim/openapi")

def _get_token() -> str:
    """Get valid token — prefers OAuth2 stored token, falls back to 24h env token."""
    try:
        from saxo_auth import get_token
        return get_token()
    except Exception:
        return os.getenv("SAXO_TOKEN", "")

# C25 tickers: our local ticker → (search keyword, Saxo ExchangeId)
# ExchangeId "CSE" = Copenhagen Stock Exchange (Saxo's code)
SAXO_SYMBOLS = {
    "NOVO-B":   ("Novo Nordisk B",          "CSE"),
    "DANSKE":   ("Danske Bank",              "CSE"),
    "DSV":      ("DSV",                      "CSE"),
    "DEMANT":   ("Demant",                   "CSE"),
    "ORSTED":   ("Orsted",                   "CSE"),
    "ROCK-B":   ("Rockwool",                 "CSE"),
    "VWS":      ("Vestas",                   "CSE"),
    "COLOB":    ("Coloplast",                "CSE"),
    "GMAB":     ("Genmab",                   "CSE"),
    "MAERSK-B": ("Maersk B",                 "CSE"),
    "NZYM-B":   ("Novozymes",                "CSE"),
    "NKT":      ("NKT",                      "CSE"),
    "GN":       ("GN Store Nord",            "CSE"),
    "BAVAR":    ("Bavarian Nordic",          "CSE"),
    "TRYG":     ("Tryg",                     "CSE"),
    "PNDORA":   ("Pandora",                  "CSE"),
    "CARL-B":   ("Carlsberg",                "CSE"),
    "CHR":      ("Chr. Hansen",              "CSE"),
    "FLS":      ("FLSmidth",                 "CSE"),
    "RBREW":    ("Royal Unibrew",            "CSE"),
    "SYDB":     ("Sydbank",                  "CSE"),
}


def _headers():
    return {"Authorization": f"Bearer {_get_token()}", "Content-Type": "application/json"}


def _get(path, params=None):
    r = requests.get(f"{BASE}{path}", headers=_headers(), params=params, timeout=10)
    r.raise_for_status()
    return r.json()


def _post(path, body):
    r = requests.post(f"{BASE}{path}", headers=_headers(), json=body, timeout=10)
    r.raise_for_status()
    return r.json()


# ── Account info ────────────────────────────────────────────────────────────

def get_account_info():
    """Return (ClientKey, AccountKey) for the SIM account."""
    data = _get("/port/v1/accounts/me")
    accounts = data.get("Data", [])
    if not accounts:
        raise RuntimeError("No accounts found — are you logged into SIM?")
    acct = accounts[0]
    return acct["ClientKey"], acct["AccountKey"]


def get_balances():
    """Return cash balance dict from Saxo SIM."""
    return _get("/port/v1/balances/me")


# ── Instrument lookup ───────────────────────────────────────────────────────

def find_uic(ticker: str) -> int | None:
    """Resolve our local ticker to Saxo UIC (instrument ID)."""
    if ticker not in SAXO_SYMBOLS:
        return None
    symbol, exchange = SAXO_SYMBOLS[ticker]
    results = _get("/ref/v1/instruments", params={
        "Keywords": symbol,
        "ExchangeId": exchange,
        "AssetTypes": "Stock",
        "$top": 5,
    })
    for item in results.get("Data", []):
        if item.get("ExchangeId") == exchange:
            return item["Identifier"]
    return None


def get_price(uic: int) -> float | None:
    """
    Get last traded price for a UIC.
    NOTE: SIM 24h tokens have no market data access — returns None.
    Use yfinance for prices instead (see signals.py).
    """
    try:
        data = _get("/trade/v1/infoprices", params={
            "Uic": uic,
            "AssetType": "Stock",
            "FieldGroups": "Quote",
        })
        last = data.get("Quote", {}).get("Last")
        return float(last) if last else None
    except Exception:
        return None


# ── Orders ──────────────────────────────────────────────────────────────────

def place_order(
    ticker: str,
    shares: int,
    direction: str = "Buy",
    *,
    price_dkk: float | None = None,
    signal_score: float | None = None,
    analyst_rec: str | None = None,
    note: str | None = None,
) -> dict:
    """
    Place a market order on Saxo SIM.

    Args:
        ticker:       Our C25 ticker (e.g. "NOVO-B")
        shares:       Number of shares
        direction:    "Buy" or "Sell"
        price_dkk:    Estimated fill price in DKK (for fee calc + logging)
        signal_score: NLP signal score that triggered this order
        analyst_rec:  Analyst recommendation label
        note:         Free-text note

    Returns:
        Saxo order response dict
    """
    _, acct_key = get_account_info()
    uic = find_uic(ticker)
    if uic is None:
        raise ValueError(f"Unknown ticker: {ticker}")

    body = {
        "Uic": uic,
        "AssetType": "Stock",
        "Amount": shares,
        "BuySell": direction,
        "OrderType": "Market",
        "OrderDuration": {"DurationType": "DayOrder"},
        "AccountKey": acct_key,
        "ManualOrder": True,
    }
    resp = _post("/trade/v2/orders", body)

    # Log to saxo_orders table regardless of outcome
    saxo_order_id = resp.get("OrderId") or resp.get("Orders", [{}])[0].get("OrderId")
    status = "filled" if saxo_order_id else "rejected"
    log_order(
        ticker=ticker,
        direction=direction.lower(),
        shares=shares,
        price_dkk=price_dkk,
        saxo_order_id=str(saxo_order_id) if saxo_order_id else None,
        status=status,
        signal_score=signal_score,
        analyst_rec=analyst_rec,
        note=note,
    )
    return resp


def log_order(
    ticker: str,
    direction: str,
    shares: float,
    price_dkk: float | None,
    saxo_order_id: str | None = None,
    status: str = "pending",
    signal_score: float | None = None,
    analyst_rec: str | None = None,
    note: str | None = None,
) -> None:
    """
    Write a trade record to saxo_orders table.

    Fee model: max(25 DKK, total_dkk * 0.001)
    """
    total_dkk = (shares * price_dkk) if price_dkk else None
    fee_dkk   = round(max(25.0, total_dkk * 0.001), 2) if total_dkk else None
    now_ts    = datetime.now(timezone.utc).isoformat()

    db = get_conn()
    db.execute(
        """INSERT INTO saxo_orders
           (ticker, direction, shares, price_dkk, total_dkk, fee_dkk,
            saxo_order_id, status, signal_score, analyst_rec, placed_at, note)
           VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
        (ticker, direction, shares, price_dkk, total_dkk, fee_dkk,
         saxo_order_id, status, signal_score, analyst_rec, now_ts, note),
    )
    db.commit()
    db.close()


# ── Positions ───────────────────────────────────────────────────────────────

def get_positions() -> list[dict]:
    """Return open positions from Saxo SIM."""
    data = _get("/port/v1/positions/me", params={"FieldGroups": "PositionBase,PositionView"})
    return data.get("Data", [])


def print_positions():
    """Pretty-print open positions."""
    positions = get_positions()
    if not positions:
        print("Ingen åbne positioner i Saxo SIM")
        return
    print(f"\n{'Ticker':<12} {'Antal':>6} {'Åbn kurs':>10} {'Akt kurs':>10} {'P/L':>10}")
    print("-" * 52)
    for p in positions:
        base = p.get("PositionBase", {})
        view = p.get("PositionView", {})
        symbol = base.get("Symbol", "?")
        amount = base.get("Amount", 0)
        open_price = base.get("OpenPrice", 0)
        current = view.get("CurrentPrice", 0)
        pl = view.get("ProfitLossOnTrade", 0)
        pl_str = f"+{pl:.0f}" if pl >= 0 else f"{pl:.0f}"
        print(f"{symbol:<12} {amount:>6} {open_price:>10.2f} {current:>10.2f} {pl_str:>10}")


def print_balances():
    """Pretty-print account balances."""
    b = get_balances()
    cash = b.get("CashBalance", 0)
    equity = b.get("TotalValue", 0)
    margin = b.get("MarginAvailableForTrading", 0)
    currency = b.get("Currency", "EUR")
    print(f"\n  Kontant saldo:     {cash:>12,.0f} {currency}")
    print(f"  Total portefølje:  {equity:>12,.0f} {currency}")
    print(f"  Fri margin:        {margin:>12,.0f} {currency}")


# ── CLI ──────────────────────────────────────────────────────────────────────

def main():
    try:
        _get_token()
    except Exception as e:
        print(f"FEJL: {e}")
        print("Kør: python saxo_auth.py login")
        sys.exit(1)

    cmd = sys.argv[1] if len(sys.argv) > 1 else "status"

    if cmd == "status":
        print("\n=== SAXO SIM KONTO ===")
        client_key, acct_key = get_account_info()
        print(f"  ClientKey: {client_key}")
        print(f"  AccountKey: {acct_key}")
        print_balances()
        print_positions()

    elif cmd == "buy":
        # saxo_broker.py buy NOVO-B 5
        if len(sys.argv) < 4:
            print("Brug: saxo_broker.py buy TICKER ANTAL")
            sys.exit(1)
        ticker = sys.argv[2].upper()
        shares = int(sys.argv[3])
        print(f"Placerer KØB {shares} × {ticker} på Saxo SIM ...")
        result = place_order(ticker, shares, "Buy")
        print(f"Ordre bekræftet: {json.dumps(result, indent=2)}")

    elif cmd == "sell":
        # saxo_broker.py sell NOVO-B 5
        if len(sys.argv) < 4:
            print("Brug: saxo_broker.py sell TICKER ANTAL")
            sys.exit(1)
        ticker = sys.argv[2].upper()
        shares = int(sys.argv[3])
        print(f"Placerer SÆLG {shares} × {ticker} på Saxo SIM ...")
        result = place_order(ticker, shares, "Sell")
        print(f"Ordre bekræftet: {json.dumps(result, indent=2)}")

    elif cmd == "positions":
        print_positions()

    elif cmd == "balances":
        print_balances()

    elif cmd == "uic":
        # saxo_broker.py uic NOVO-B
        ticker = sys.argv[2].upper() if len(sys.argv) > 2 else "NOVO-B"
        uic = find_uic(ticker)
        price = get_price(uic) if uic else None
        print(f"{ticker}: UIC={uic}, pris={price}")

    else:
        print(f"Ukendt kommando: {cmd}")
        print("Kommandoer: status | buy TICKER N | sell TICKER N | positions | balances | uic TICKER")
        sys.exit(1)


if __name__ == "__main__":
    main()
