"""
runner.py — MoneyMaker pipeline orchestrator.

Usage:
    python runner.py                    # full pipeline (fetch → analyze → evaluate → execute)
    python runner.py --dry-run          # simulate everything, no real orders
    python runner.py --analyze-only     # only fetch news + run NLP, no orders
    python runner.py --report           # print P&L report only

Pipeline flow (each run):
  1. Fetch Ground News + Danish RSS feeds
  2. Run NLP+Claude analysis on new articles
  3. Evaluate buy/sell signals vs open positions
  4. Execute orders on Saxo SIM (skip if --dry-run)
  5. Print status summary

Scheduled by cron:
  06:00  CET  --analyze-only   (full NLP before market opens)
  09:30  CET  (default)        trade window 1
  12:00  CET  (default)        trade window 2
  14:30  CET  (default)        trade window 3
  16:30  CET  (default)        trade window 4
  19:00  CET  --report         daily P&L summary
"""
import sys
import argparse
import traceback
from datetime import datetime, timezone

from db import get_conn, DB_TYPE, init_schema
from ground_news import fetch_all
from rss_feeds import fetch_all_rss
from analyze import analyze_articles
from portfolio import evaluate_orders, get_db as portfolio_db, _open_positions
from report import print_report


def run_pipeline(dry_run: bool = False, analyze_only: bool = False) -> None:
    ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
    print(f"\n{'═'*60}")
    print(f"  MONEYMAKER RUNNER  ·  {ts}")
    print(f"  Mode: {'DRY RUN' if dry_run else 'LIVE'}  ·  DB: {DB_TYPE}")
    print(f"{'═'*60}\n")

    # ── 1. Ensure schema is up-to-date ──────────────────────────────────────
    try:
        init_schema()
    except Exception as e:
        print(f"[runner] WARNING: init_schema() failed: {e}")

    # ── 2. Fetch news ────────────────────────────────────────────────────────
    db = get_conn()
    try:
        print("[runner] Henter Ground News …")
        n_gn = fetch_all(db)
        print(f"  +{n_gn} nye artikler fra Ground News")

        print("[runner] Henter danske RSS feeds …")
        n_rss = fetch_all_rss(db)
        print(f"  +{n_rss} nye artikler fra RSS feeds")
    finally:
        db.close()

    # ── 3. NLP analysis ───────────────────────────────────────────────────────
    print("\n[runner] Kører NLP analyse …")
    analyze_articles(
        force=False,
        dry_run=dry_run,
        use_claude=True,
        auto_fetch=False,  # already fetched above
    )

    if analyze_only:
        print("\n[runner] --analyze-only flag sat. Stopper efter NLP.")
        return

    # ── 4. Evaluate orders ────────────────────────────────────────────────────
    print("\n[runner] Evaluerer ordre-forslag …")
    pdb = portfolio_db()
    try:
        evaluate_orders(pdb)
    finally:
        pdb.close()

    # ── 5. Auto-execute (skip if dry-run) ────────────────────────────────────
    if not dry_run:
        _auto_execute()
    else:
        print("\n[runner] DRY RUN — ingen ordre placeret.")

    print("\n[runner] Pipeline færdig.\n")


def _auto_execute() -> None:
    """
    Auto-place orders on Saxo SIM based on portfolio recommendations.

    1. AUTO-SELL: close positions that have hit stop-loss or take-profit.
    2. AUTO-BUY: open new positions when signal + analyst consensus align.

    Orders are placed via saxo_broker.place_order() and logged to saxo_orders.
    Sells record signal_correct in position_events.
    """
    from portfolio import (
        CAPITAL, MAX_POSITIONS, MIN_SIGNAL, BUY_ANALYST,
        _open_positions, _open_count, _best_signals, _per_position, _c25_map,
        cmd_sell,
    )
    from saxo_broker import place_order, find_uic
    import yfinance as yf

    pdb = portfolio_db()
    c25 = _c25_map()

    # ── 1. Auto-sell: stop-loss / take-profit ─────────────────────────────
    for pos in _open_positions(pdb):
        ticker    = pos["ticker"]
        yf_ticker = c25.get(ticker, {}).get("ticker_yahoo", ticker + ".CO")
        try:
            price = yf.Ticker(yf_ticker).fast_info.get("lastPrice")
        except Exception as e:
            print(f"  SKIP SELL {ticker} — kurs fejl: {e}")
            continue

        if not price:
            continue

        stop_hit = price <= pos["stop_loss"]
        take_hit = price >= pos["take_profit"]

        if stop_hit or take_hit:
            reason = "stop-loss" if stop_hit else "take-profit"
            print(f"  → SÆLGER {pos['shares']:.0f} stk {ticker} à {price:.2f} kr ({reason})")
            try:
                resp = place_order(
                    ticker, pos["shares"], "Sell",
                    price_dkk=price,
                    note=f"runner auto-sell {reason}",
                )
                print(f"    Saxo svar: {resp}")
            except Exception as e:
                print(f"    FEJL ved Saxo ordre: {e}")
            # Record sell in portfolio DB (sets signal_correct)
            cmd_sell(pdb, [ticker, str(price)])

    # ── 2. Auto-buy: new positions ────────────────────────────────────────
    signals  = _best_signals(pdb)
    open_pos = {p["ticker"]: p for p in _open_positions(pdb)}

    for sig in signals:
        ticker      = sig["ticker"]
        score       = sig["signal_score"] or 0
        momentum    = sig["momentum_dir"] or "unknown"

        # Only buy if score is strong + not already in position
        if score < MIN_SIGNAL:
            continue
        if ticker in open_pos:
            continue
        if len(open_pos) >= MAX_POSITIONS:
            break

        # Check analyst recommendation
        try:
            from signals import analyst_rec
            rec = analyst_rec(ticker)
            rec_label_lower = rec["label"].lower().replace("🟢", "").strip()
            if rec_label_lower not in BUY_ANALYST:
                print(f"  SKIP {ticker} — analyst={rec['label']}")
                continue
        except Exception as e:
            print(f"  SKIP {ticker} — analyst check failed: {e}")
            continue

        # Get current price via yfinance
        try:
            yf_ticker = c25.get(ticker, {}).get("ticker_yahoo", ticker + ".CO")
            price = yf.Ticker(yf_ticker).fast_info.get("lastPrice")
            if not price:
                print(f"  SKIP {ticker} — ingen kurs fra yfinance")
                continue
        except Exception as e:
            print(f"  SKIP {ticker} — kurs fejl: {e}")
            continue

        per_pos = _per_position(pdb)
        if per_pos < 500:
            print(f"  SKIP {ticker} — for lidt kapital: {per_pos} DKK")
            continue

        shares = int(per_pos // price)
        if shares < 1:
            print(f"  SKIP {ticker} — 0 aktier til {price:.2f} DKK/stk")
            continue

        print(f"  → KØBER {shares} stk {ticker} à ~{price:.2f} DKK (sig={score:.3f})")
        try:
            resp = place_order(
                ticker, shares, "Buy",
                price_dkk=price,
                signal_score=score,
                analyst_rec=rec["label"],
                note=f"runner auto-buy sig={score:.3f} mom={momentum}",
            )
            print(f"    Saxo svar: {resp}")
        except Exception as e:
            print(f"    FEJL ved ordre: {e}")
        open_pos[ticker] = {"ticker": ticker}  # prevent double-buy in same run

    pdb.close()


def main():
    parser = argparse.ArgumentParser(description="MoneyMaker pipeline runner")
    parser.add_argument("--dry-run",      action="store_true", help="Simulate, no real orders")
    parser.add_argument("--analyze-only", action="store_true", help="Only fetch + NLP, no orders")
    parser.add_argument("--report",       action="store_true", help="Print P&L report and exit")
    args = parser.parse_args()

    if args.report:
        print_report()
        return

    try:
        run_pipeline(dry_run=args.dry_run, analyze_only=args.analyze_only)
    except KeyboardInterrupt:
        print("\n[runner] Afbrudt.")
    except Exception:
        print("[runner] FEJL:")
        traceback.print_exc()
        sys.exit(1)


if __name__ == "__main__":
    main()
