"""체결 추적 on-demand 데몬 entry point.

알바(handler.submit_with_pin)가 fork 한 자식 프로세스에서 실행됨.

수명:
1. PID 파일 atomic 작성 (이미 살아있는 데몬 있으면 즉시 종료)
2. 큐 파일 → _FillWatcher._tracked 동기화
3. kt00007 폴링 → 체결/거절/타임아웃 알림 → 추적 종료된 ord_no 큐에서 제거
4. 큐 비면 PID 파일 삭제 + sys.exit(0)
5. 다음 매매 시 알바가 다시 fork

CLI 직접 호출은 하지 않음. 운영은 알바의 ensure_daemon_running() 만.
"""

from __future__ import annotations

import fcntl
import json
import os
import signal
import sys
import time
import traceback
from contextlib import contextmanager
from pathlib import Path

# 패키지 import 경로 보장 — 알바가 cwd=scripts/ 로 띄움
_SCRIPTS_DIR = Path(__file__).resolve().parent.parent
if str(_SCRIPTS_DIR) not in sys.path:
    sys.path.insert(0, str(_SCRIPTS_DIR))

from orders import fill_watcher  # noqa: E402

LOG_FILE = fill_watcher._STATE_DIR / 'fill_watcher.log'
PID_LOCK = fill_watcher._STATE_DIR / 'fill_watcher.pid.lock'

_shutdown = False


def _log(msg: str) -> None:
    LOG_FILE.parent.mkdir(parents=True, exist_ok=True)
    ts = time.strftime('%Y-%m-%dT%H:%M:%S%z', time.localtime())
    try:
        with LOG_FILE.open('a', encoding='utf-8') as f:
            f.write(f'[{ts}] {msg}\n')
    except OSError:
        pass


@contextmanager
def _pid_lock():
    PID_LOCK.parent.mkdir(parents=True, exist_ok=True)
    fp = open(PID_LOCK, 'w')
    try:
        fcntl.flock(fp, fcntl.LOCK_EX)
        yield
    finally:
        try:
            fcntl.flock(fp, fcntl.LOCK_UN)
        finally:
            fp.close()


def _claim_pid() -> bool:
    """PID 파일 atomic 작성. 이미 살아있는 데몬 있으면 False."""
    with _pid_lock():
        if fill_watcher.is_daemon_alive():
            return False
        fill_watcher.PID_FILE.parent.mkdir(parents=True, exist_ok=True)
        fill_watcher.PID_FILE.write_text(str(os.getpid()), encoding='utf-8')
        return True


def _release_pid() -> None:
    try:
        if fill_watcher.PID_FILE.exists():
            try:
                pid = int(fill_watcher.PID_FILE.read_text().strip())
            except (ValueError, OSError):
                pid = -1
            if pid == os.getpid():
                fill_watcher.PID_FILE.unlink()
    except OSError:
        pass


def _handle_sigterm(signum, frame):
    global _shutdown
    _shutdown = True


def _build_telegram_sender():
    # 순환 import 방지 — 데몬에서만 import
    from orders import handler
    return lambda msg: handler.send_telegram(msg, parse_mode=None)


def _build_kiwoom_fetcher():
    import kiwoom_client as kc
    return lambda account: kc.get_order_executions(account)


def _build_kiwoom_open_orders_fetcher():
    import kiwoom_client as kc
    return lambda account: kc.get_open_orders(account)


def main() -> int:
    if not _claim_pid():
        _log('skip — daemon already running')
        return 0

    signal.signal(signal.SIGTERM, _handle_sigterm)
    signal.signal(signal.SIGINT, _handle_sigterm)

    fill_watcher.configure(
        send_func=_build_telegram_sender(),
        fetch_executions=_build_kiwoom_fetcher(),
        fetch_open_orders=_build_kiwoom_open_orders_fetcher(),
    )
    _log(f'started pid={os.getpid()}')

    try:
        while not _shutdown:
            queue = fill_watcher.read_queue()
            if not queue:
                _log('queue empty — exiting')
                return 0
            fill_watcher._watcher.sync_from_queue(queue)
            try:
                fill_watcher._watcher._poll_once()
            except Exception:
                _log('poll error:\n' + traceback.format_exc())
            # 추적 종료된 entry 큐에서 제거
            fill_watcher.persist_queue(fill_watcher._watcher.snapshot_entries())
            time.sleep(fill_watcher._watcher._next_sleep_seconds())
        _log('shutdown signal — exiting')
        return 0
    finally:
        _release_pid()


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