"""
Record a LoL event's main orderbooks and live-data activity trades together.

This is the preferred entrypoint for event studies. It records:
    - CLOB orderbook events/snapshots for selected winner markets
    - ws-live-data activity trades for the same selected markets
"""
from __future__ import annotations

import asyncio
import json
import os
import signal
import sys
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo

import aiohttp
import click
from dotenv import load_dotenv
from loguru import logger

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT not in sys.path:
    sys.path.insert(0, ROOT)

from analysis.lpl_orderbook.recorder import LplEventOrderbookRecorder  # noqa: E402
from scripts.record_lpl_activity_trades import run_recorder as run_activity_recorder  # noqa: E402
from scripts.record_lpl_event_orderbook import _market_set  # noqa: E402


SHANGHAI_TZ = ZoneInfo("Asia/Shanghai")


def _setup_logging() -> None:
    logger.remove()
    logger.add(
        sys.stderr,
        level=os.environ.get("LOG_LEVEL", "INFO").upper(),
        format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level:<7}</level> | {message}",
        colorize=True,
    )


def _file_size(path: Path) -> int:
    try:
        return path.stat().st_size
    except FileNotFoundError:
        return 0


def _load_json(path: Path) -> dict[str, object]:
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (FileNotFoundError, json.JSONDecodeError):
        return {}


def _mb(size: int) -> str:
    return f"{size / 1024 / 1024:.2f} MB"


def _fmt_dt(value: object) -> str:
    if not value:
        return ""
    try:
        return datetime.fromisoformat(str(value)).astimezone(SHANGHAI_TZ).strftime(
            "%Y-%m-%d %H:%M:%S"
        )
    except ValueError:
        return str(value)


def _status_text(
    *,
    event_slug: str,
    orderbook_output_root: Path,
    activity_output_root: Path,
    phase: str,
) -> str:
    orderbook_dir = orderbook_output_root / event_slug
    activity_dir = activity_output_root / event_slug
    status = _load_json(orderbook_dir / "recording_status.json")
    target_markets = _load_json(activity_dir / "target_markets.json")
    markets = ""
    if isinstance(target_markets, list):
        markets = ", ".join(
            str(item.get("kind") or item.get("slug") or "")
            for item in target_markets
            if isinstance(item, dict)
        )

    now = datetime.now(SHANGHAI_TZ).strftime("%Y-%m-%d %H:%M:%S")
    lines = [
        f"LoL 采集状态 [{phase}]",
        f"event: {event_slug}",
        f"时间(东八区): {now}",
        f"markets: {markets or 'loading'}",
        f"status: {status.get('status', 'starting')}",
        f"token_count: {status.get('token_count', '')}",
        f"connection_id: {status.get('connection_id', '')}",
        f"reconnect_count: {status.get('reconnect_count', '')}",
        f"events_written: {status.get('events_written', '')}",
        f"snapshots_written: {status.get('snapshots_written', '')}",
        f"last_event_type: {status.get('last_event_type', '')}",
        f"last_received_at: {_fmt_dt(status.get('last_received_at_wall'))}",
        f"last_message_age_seconds: {status.get('last_message_age_seconds', '')}",
        f"orderbook_events: {_mb(_file_size(orderbook_dir / 'orderbook_events.jsonl'))}",
        f"orderbook_snapshots: {_mb(_file_size(orderbook_dir / 'orderbook_snapshots.jsonl'))}",
        f"activity_trades: {_mb(_file_size(activity_dir / 'activity_trades.jsonl'))}",
    ]
    return "\n".join(lines)


async def _send_feishu_text(webhook: str, text: str) -> None:
    if not webhook:
        return
    payload = {"msg_type": "text", "content": {"text": text}}
    timeout = aiohttp.ClientTimeout(total=10)
    async with aiohttp.ClientSession(timeout=timeout) as http:
        async with http.post(webhook, json=payload) as resp:
            if resp.status >= 400:
                body = await resp.text()
                logger.warning(f"飞书通知失败: status={resp.status} body={body[:300]}")


async def _feishu_status_loop(
    *,
    webhook: str,
    event_slug: str,
    orderbook_output_root: Path,
    activity_output_root: Path,
    interval_seconds: float,
    stop_event: asyncio.Event,
) -> None:
    if not webhook:
        return
    await _send_feishu_text(
        webhook,
        _status_text(
            event_slug=event_slug,
            orderbook_output_root=orderbook_output_root,
            activity_output_root=activity_output_root,
            phase="started",
        ),
    )
    while not stop_event.is_set():
        try:
            await asyncio.wait_for(stop_event.wait(), timeout=interval_seconds)
        except asyncio.TimeoutError:
            await _send_feishu_text(
                webhook,
                _status_text(
                    event_slug=event_slug,
                    orderbook_output_root=orderbook_output_root,
                    activity_output_root=activity_output_root,
                    phase="running",
                ),
            )


async def _run_capture(
    *,
    event_slug: str,
    markets: set[str] | None,
    orderbook_output_root: Path,
    activity_output_root: Path,
    snapshot_interval_seconds: float,
    insecure_ssl: bool,
    status_interval_seconds: float,
    jsonl_flush_every: int,
    jsonl_flush_interval_seconds: float,
    feishu_webhook: str,
    feishu_interval_seconds: float,
    stop_event: asyncio.Event,
) -> None:
    orderbook = LplEventOrderbookRecorder(
        event_slug=event_slug,
        markets=markets,
        output_root=orderbook_output_root,
        snapshot_interval_seconds=snapshot_interval_seconds,
        insecure_ssl=insecure_ssl,
        status_interval_seconds=status_interval_seconds,
        jsonl_flush_every=jsonl_flush_every,
        jsonl_flush_interval_seconds=jsonl_flush_interval_seconds,
    )
    orderbook_task = asyncio.create_task(orderbook.run(), name="lpl_event_orderbook")
    activity_task = asyncio.create_task(
        run_activity_recorder(
            event_slug=event_slug,
            markets=markets,
            output_root=activity_output_root,
            include_comments=False,
            comments_parent_id="",
            insecure_ssl=insecure_ssl,
            reconnect_delay_seconds=3.0,
            jsonl_flush_every=jsonl_flush_every,
            jsonl_flush_interval_seconds=jsonl_flush_interval_seconds,
            stop_event=stop_event,
        ),
        name="lpl_event_activity",
    )
    notify_task = (
        asyncio.create_task(
            _feishu_status_loop(
                webhook=feishu_webhook,
                event_slug=event_slug,
                orderbook_output_root=orderbook_output_root,
                activity_output_root=activity_output_root,
                interval_seconds=feishu_interval_seconds,
                stop_event=stop_event,
            ),
            name="lpl_event_feishu_notify",
        )
        if feishu_webhook
        else None
    )

    def stop_all() -> None:
        stop_event.set()
        orderbook.stop()

    try:
        while not stop_event.is_set():
            tasks = {orderbook_task, activity_task}
            if notify_task is not None:
                tasks.add(notify_task)
            done, _pending = await asyncio.wait(
                tasks,
                timeout=1.0,
                return_when=asyncio.FIRST_EXCEPTION,
            )
            for task in done:
                if task is notify_task:
                    exc = task.exception()
                    if exc is not None:
                        logger.warning(f"飞书通知任务停止: {exc}")
                    notify_task = None
                    continue
                if task.cancelled():
                    stop_all()
                    return
                exc = task.exception()
                if exc is not None:
                    stop_all()
                    raise exc
                stop_all()
                return
    finally:
        stop_all()
        try:
            tasks = [orderbook_task, activity_task]
            if notify_task is not None:
                tasks.append(notify_task)
            await asyncio.wait_for(
                asyncio.gather(*tasks, return_exceptions=True),
                timeout=10.0,
            )
        except asyncio.TimeoutError:
            tasks = [orderbook_task, activity_task]
            if notify_task is not None:
                tasks.append(notify_task)
            for task in tasks:
                task.cancel()
            await asyncio.gather(*tasks, return_exceptions=True)
        if feishu_webhook:
            await _send_feishu_text(
                feishu_webhook,
                _status_text(
                    event_slug=event_slug,
                    orderbook_output_root=orderbook_output_root,
                    activity_output_root=activity_output_root,
                    phase="stopped",
                ),
            )


@click.command()
@click.option("--event-slug", required=True, help="Polymarket event slug")
@click.option(
    "--markets",
    default="auto",
    show_default=True,
    help="auto, or comma-separated target markets through game4.",
)
@click.option(
    "--orderbook-output-root",
    default="data/lpl",
    show_default=True,
    type=click.Path(file_okay=False, path_type=Path),
)
@click.option(
    "--activity-output-root",
    default="data/lpl_events",
    show_default=True,
    type=click.Path(file_okay=False, path_type=Path),
)
@click.option("--snapshot-interval-seconds", default=0.5, show_default=True, type=float)
@click.option("--insecure-ssl", is_flag=True)
@click.option("--status-interval-seconds", default=5.0, show_default=True, type=float)
@click.option("--jsonl-flush-every", default=100, show_default=True, type=int)
@click.option("--jsonl-flush-interval-seconds", default=0.5, show_default=True, type=float)
@click.option(
    "--feishu-webhook",
    default="",
    help="Feishu bot webhook. Defaults to FEISHU_WEBHOOK_URL env when omitted.",
)
@click.option("--feishu-interval-seconds", default=300.0, show_default=True, type=float)
def main(
    event_slug: str,
    markets: str,
    orderbook_output_root: Path,
    activity_output_root: Path,
    snapshot_interval_seconds: float,
    insecure_ssl: bool,
    status_interval_seconds: float,
    jsonl_flush_every: int,
    jsonl_flush_interval_seconds: float,
    feishu_webhook: str,
    feishu_interval_seconds: float,
) -> None:
    load_dotenv()
    _setup_logging()
    selected_markets = _market_set(markets)
    feishu_webhook = feishu_webhook or os.getenv("FEISHU_WEBHOOK_URL", "")
    stop_event = asyncio.Event()
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    def stop() -> None:
        stop_event.set()

    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.add_signal_handler(sig, stop)

    logger.info(
        f"开始录制 LoL event: event_slug={event_slug} markets={markets}"
    )
    try:
        loop.run_until_complete(
            _run_capture(
                event_slug=event_slug,
                markets=selected_markets,
                orderbook_output_root=orderbook_output_root,
                activity_output_root=activity_output_root,
                snapshot_interval_seconds=snapshot_interval_seconds,
                insecure_ssl=insecure_ssl,
                status_interval_seconds=status_interval_seconds,
                jsonl_flush_every=jsonl_flush_every,
                jsonl_flush_interval_seconds=jsonl_flush_interval_seconds,
                feishu_webhook=feishu_webhook,
                feishu_interval_seconds=feishu_interval_seconds,
                stop_event=stop_event,
            )
        )
    finally:
        loop.close()
    logger.info(f"录制已停止: event_slug={event_slug}")


if __name__ == "__main__":
    main()
