from __future__ import annotations

import logging
import threading
from typing import Optional

from .config import AppConfig
from .storage import Storage
from .util import atomic_write_json, parse_iso, to_iso, utcnow


class StatusScheduler:
    def __init__(self, storage: Storage, config: AppConfig, logger: Optional[logging.Logger] = None) -> None:
        self._storage = storage
        self._config = config
        self._logger = logger or logging.getLogger("argus.master.scheduler")
        self._stop_event = threading.Event()
        self._thread = threading.Thread(target=self._run, name="status-scheduler", daemon=True)
        self._nodes_json_lock = threading.Lock()
        self._pending_nodes_json = threading.Event()

    def start(self) -> None:
        """启动后台线程，定期刷新节点状态与 nodes.json。"""
        if not self._thread.is_alive():
            self._logger.info("Starting scheduler thread")
            self._thread.start()

    def stop(self) -> None:
        self._stop_event.set()
        self._pending_nodes_json.set()
        self._thread.join(timeout=5)

    def trigger_nodes_json_refresh(self) -> None:
        self._pending_nodes_json.set()

    def generate_nodes_json(self) -> None:
        with self._nodes_json_lock:
            online_nodes = self._storage.get_online_nodes()
            atomic_write_json(self._config.metric_nodes_json_path, online_nodes)
            self._logger.info("nodes.json updated", extra={"count": len(online_nodes)})

    # ------------------------------------------------------------------
    # internal loop
    # ------------------------------------------------------------------

    def _run(self) -> None:
        # 确保启动时 nodes.json 会立即生成
        self._pending_nodes_json.set()
        while not self._stop_event.is_set():
            changed = self._reconcile_statuses()
            if changed or self._pending_nodes_json.is_set():
                try:
                    self.generate_nodes_json()
                finally:
                    self._pending_nodes_json.clear()
            self._stop_event.wait(self._config.scheduler_interval_seconds)

    def _reconcile_statuses(self) -> bool:
        """根据 last_report 与当前时间对比，决定是否切换状态。"""
        any_status_changed = False
        now = utcnow()
        rows = self._storage.fetch_nodes_for_scheduler()
        for row in rows:
            node_id = row["id"]
            last_report_iso = row["last_report"]
            current_status = row["status"]
            last_report_dt = parse_iso(last_report_iso)
            if last_report_dt is None:
                # No report yet; treat as initialized until report arrives
                continue
            delta_seconds = (now - last_report_dt).total_seconds()
            new_status = current_status
            if delta_seconds > self._config.offline_threshold_seconds:
                new_status = "offline"
            elif delta_seconds <= self._config.online_threshold_seconds:
                new_status = "online"
            # Between thresholds: keep current status (sticky)
            if new_status != current_status:
                any_status_changed = True
                self._logger.info(
                    "Updating node status",
                    extra={
                        "node_id": node_id,
                        "previous": current_status,
                        "new": new_status,
                        "delta_seconds": delta_seconds,
                    },
                )
                self._storage.update_status(node_id, new_status, last_updated_iso=to_iso(now))
        return any_status_changed
