"""ORB auto-scheduler standalone daemon.

Runs independently of the web server so HMR/server restarts don't kill the scheduler.
Started and stopped by the web API via subprocess management.

Usage (usually via web API, but can be run manually):
    python -m apps.orb_trader.daemon --db-path data/paper/orb.db
    python -m apps.orb_trader.daemon --db-path data/paper/orb.db --sessions "Default,Session2"
    python -m apps.orb_trader.daemon --db-path data/paper/orb.db --dry-run
"""
from __future__ import annotations

import argparse
import asyncio
import json
import logging
import time
from pathlib import Path


def main() -> None:
    parser = argparse.ArgumentParser(description="ORB auto-scheduler daemon")
    parser.add_argument("--sessions", default="",
                        help="Comma-separated session names (empty = all active)")
    parser.add_argument("--db-path", required=True, help="Path to ORB SQLite DB")
    parser.add_argument("--dry-run", action="store_true", help="Dry run (no real orders)")
    args = parser.parse_args()

    # Route Python logger output to a file so engine logs are not lost
    # (daemon's stdout/stderr are /dev/null when started from the web server)
    log_file = Path(args.db_path).parent / "orb_daemon.log"
    file_handler = logging.FileHandler(log_file, encoding="utf-8")
    file_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s"))
    logging.root.addHandler(file_handler)
    logging.root.setLevel(logging.INFO)
    # Mirror all ORB daemon logs to events.db so the Logs/Health UI can surface them.
    # Two channels:
    #   1. structlog sink processor — captures structured engine emits (orb_engine_*)
    #   2. EventsLoggingHandler — bridges plain stdlib log.info(...) lines
    try:
        from apps.web.services.events_store import EventsStore, EventsLoggingHandler
        from libs.common.logging import configure_logging
        EventsStore.get().ensure_schema()
        configure_logging(enable_events_sink=True)
        _events_handler = EventsLoggingHandler()
        _events_handler.setLevel(logging.INFO)
        logging.root.addHandler(_events_handler)
    except Exception:
        pass  # events.db mirror is best-effort; don't break the daemon

    sessions = [s.strip() for s in args.sessions.split(",") if s.strip()]
    trigger_dir = Path(args.db_path).parent

    async def _run() -> None:
        from apps.web.orb_trading_service import ORBAutoScheduler
        sched = ORBAutoScheduler()
        sched.start(sessions=sessions, db_path=args.db_path, dry_run=args.dry_run)

        async def _poll_triggers() -> None:
            """Pick up run_session_now commands written by the web API."""
            while True:
                await asyncio.sleep(3)
                for tf in sorted(trigger_dir.glob(".orb_trigger_*.json")):
                    try:
                        raw = tf.read_text()
                        tf.unlink()
                        cmd = json.loads(raw)
                        # Ignore stale triggers (> 60 s old)
                        if time.time() - cmd.get("ts", 0) > 60:
                            continue
                        if cmd.get("command") == "run_session_now":
                            session = cmd.get("session", "")
                            if session:
                                asyncio.create_task(sched.run_session_now(session))
                    except Exception:
                        pass

        asyncio.create_task(_poll_triggers())

        try:
            if sched._task:
                await sched._task
        except asyncio.CancelledError:
            pass

    try:
        asyncio.run(_run())
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()
