#!/usr/bin/env python3
"""
Phase C 슈퍼바이저: 06 dry-run 상시 + 매일 22:00 중간보고 + 금요일 22:00 최종 후 종료.

사용:
  python scripts/08_phase_c_supervisor.py
  python scripts/08_phase_c_supervisor.py --end-date 2026-06-05 --report-hour 22
"""
from __future__ import annotations

import argparse
import runpy
import signal
import subprocess
import sys
import time
from datetime import date, datetime, timedelta
from pathlib import Path

_ROOT = Path(__file__).resolve().parents[1]
runpy.run_path(str(_ROOT / "scripts" / "_bootstrap.py"))

from config import LIVE_TRADING_ENABLED  # noqa: E402
from deepcoin.paths import (  # noqa: E402
    PHASE_C_SUPERVISOR_LOG,
    PHASE_C_SUPERVISOR_PID,
)

_DEFAULT_PY = "/Users/dsyoon/opt/anaconda3/envs/coin/bin/python"
_REPORT_WINDOW_MIN = 5


def _log(msg: str) -> None:
    """슈퍼바이저 로그 (파일; nohup 시 stdout 중복 방지)."""
    line = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {msg}"
    PHASE_C_SUPERVISOR_LOG.parent.mkdir(parents=True, exist_ok=True)
    with PHASE_C_SUPERVISOR_LOG.open("a", encoding="utf-8") as f:
        f.write(line + "\n")


def _run_script(py: str, name: str, *args: str) -> int:
    """scripts/ 하위 스크립트 실행."""
    cmd = [py, str(_ROOT / "scripts" / name), *args]
    _log(f"실행: {' '.join(cmd)}")
    proc = subprocess.run(cmd, cwd=str(_ROOT), check=False)
    return proc.returncode


def _in_report_window(now: datetime, hour: int) -> bool:
    """보고 시각(시) 직후 REPORT_WINDOW_MIN 분 이내."""
    return now.hour == hour and now.minute < _REPORT_WINDOW_MIN


def _stop_child(proc: subprocess.Popen[bytes] | None) -> None:
    """06 자식 프로세스 종료."""
    if proc is None or proc.poll() is not None:
        return
    _log(f"06 종료 요청 pid={proc.pid}")
    proc.send_signal(signal.SIGTERM)
    try:
        proc.wait(timeout=15)
    except subprocess.TimeoutExpired:
        proc.kill()
        proc.wait(timeout=5)
    _log("06 종료 완료")


def _write_pid() -> None:
    """슈퍼바이저 PID 기록."""
    PHASE_C_SUPERVISOR_PID.parent.mkdir(parents=True, exist_ok=True)
    PHASE_C_SUPERVISOR_PID.write_text(str(os_getpid()), encoding="utf-8")


def os_getpid() -> int:
    """현재 PID."""
    import os

    return os.getpid()


def _daily_pipeline(py: str, *, final: bool) -> None:
    """다운로드 → verify → 07 보고."""
    _run_script(py, "01_download.py")
    _run_script(py, "06_verify_live_dryrun.py")
    kind = "final" if final else "daily"
    _run_script(py, "07_phase_c_paper_report.py", "--kind", kind)


def main() -> int:
    """
    Phase C 슈퍼바이저 메인.

    Returns:
        종료 코드 0=정상, 1=설정 오류.
    """
    parser = argparse.ArgumentParser(description="Phase C dry-run 슈퍼바이저")
    parser.add_argument(
        "--end-date",
        type=lambda s: date.fromisoformat(s),
        default=date(2026, 6, 5),
        help="최종 보고·종료일 (금요일, ISO)",
    )
    parser.add_argument("--report-hour", type=int, default=22, help="일일 보고 시각(시, 24h)")
    parser.add_argument(
        "--py",
        default=_DEFAULT_PY,
        help="Python 실행 파일 (coin conda)",
    )
    args = parser.parse_args()

    if LIVE_TRADING_ENABLED:
        _log("오류: LIVE_TRADING_ENABLED=1 — Phase C는 0 이어야 합니다.")
        return 1

    _write_pid()
    reported: set[date] = set()
    py = args.py
    end: date = args.end_date
    hour: int = args.report_hour

    _log(
        f"Phase C 슈퍼바이저 시작 · end={end} · 보고 {hour}:00 KST · LIVE=0"
    )

    child = subprocess.Popen(
        [py, str(_ROOT / "scripts" / "06_execute_live.py")],
        cwd=str(_ROOT),
    )
    _log(f"06 dry-run 기동 pid={child.pid}")

    try:
        while True:
            now = datetime.now()
            today = now.date()

            if child.poll() is not None:
                _log(f"06 비정상 종료 code={child.returncode} — 재기동")
                child = subprocess.Popen(
                    [py, str(_ROOT / "scripts" / "06_execute_live.py")],
                    cwd=str(_ROOT),
                )

            if _in_report_window(now, hour) and today not in reported:
                if today <= end:
                    reported.add(today)
                    is_final = today == end
                    _log(
                        f"{'최종' if is_final else '중간'} 보고 시작 ({today})"
                    )
                    _daily_pipeline(py, final=is_final)
                    if is_final:
                        _log("금요일 최종 보고 완료 — Phase C dry-run 종료")
                        break

            if today > end and today not in reported:
                _log("종료일 경과 — 최종 보고(미실시 시) 후 종료")
                _daily_pipeline(py, final=True)
                break

            time.sleep(60)
    except KeyboardInterrupt:
        _log("KeyboardInterrupt — 종료")
    finally:
        _stop_child(child)
        if PHASE_C_SUPERVISOR_PID.is_file():
            PHASE_C_SUPERVISOR_PID.unlink(missing_ok=True)
        _log("슈퍼바이저 종료")

    return 0


if __name__ == "__main__":
    sys.exit(main())
