import gc
import os
import sys
import tempfile

_fd, _TMP = tempfile.mkstemp(suffix=".db")
os.close(_fd)
os.unlink(_TMP)
os.environ["AGENT_OFFICE_DB_PATH"] = _TMP

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import pytest

from app.curator import signal_runner
from app import db

db.DB_PATH = _TMP  # patch frozen module-level DB_PATH (import order safety)


@pytest.fixture(autouse=True)
def fresh_db():
    gc.collect()
    if os.path.exists(_TMP):
        os.remove(_TMP)
    db.init_db()
    yield
    gc.collect()
    if os.path.exists(_TMP):
        try:
            os.remove(_TMP)
        except PermissionError:
            pass  # Windows: WAL-mode file locked; DB is ephemeral anyway


def test_evaluate_and_persist_cold_start():
    """첫 호출은 warmup으로 기록되고 baseline에 값이 들어간다."""
    result = signal_runner.evaluate_metric_and_persist(
        source="light",
        metric="sim_signal",
        value=1.5,
        draw_no=None,
        z_normal=1.5,
        z_urgent=2.5,
        push_to_window=True,
    )
    assert result["fire_level"] == "warmup"
    assert result["z_score"] is None

    bl = db.get_baseline("sim_signal")
    assert bl is not None
    assert bl["window_values"] == [1.5]


def test_evaluate_after_window_filled_normal_fire():
    """8회 push 후 정상 운영, 평균 대비 z≥1.5면 normal."""
    for v in [1.0, 1.1, 0.9, 1.0, 1.0, 1.1, 0.9, 1.0]:
        signal_runner.evaluate_metric_and_persist(
            source="sim",
            metric="sim_signal",
            value=v,
            draw_no=None,
            z_normal=1.5,
            z_urgent=2.5,
            push_to_window=True,
        )

    result = signal_runner.evaluate_metric_and_persist(
        source="sim",
        metric="sim_signal",
        value=1.12,
        draw_no=None,
        z_normal=1.5,
        z_urgent=2.5,
        push_to_window=True,
    )
    assert result["fire_level"] in ("normal", "urgent")
    assert result["z_score"] is not None and result["z_score"] >= 1.5


def test_evaluate_drift_skips_same_draw_push():
    """drift는 회차 단위. 같은 회차에서 두 번 호출하면 두 번째는 window push X."""
    signal_runner.evaluate_metric_and_persist(
        source="sim", metric="drift", value=0.05, draw_no=1100,
        z_normal=1.5, z_urgent=2.5, push_to_window=True,
    )
    bl_before = db.get_baseline("drift")
    assert bl_before["window_values"] == [0.05]
    assert bl_before["last_pushed_draw_no"] == 1100

    signal_runner.evaluate_metric_and_persist(
        source="sim", metric="drift", value=0.08, draw_no=1100,
        z_normal=1.5, z_urgent=2.5, push_to_window=True,
    )
    bl_after = db.get_baseline("drift")
    assert bl_after["window_values"] == [0.05]


@pytest.mark.asyncio
async def test_run_signal_check_aggregates_three_metrics(monkeypatch):
    """run_signal_check이 3종 메트릭 모두 평가하고 overall fire를 반환."""
    async def fake_lotto_best():
        return [{"numbers": [1,2,3,4,5,6], "scores": [10,10,10,10,10]}] * 20

    async def fake_lotto_strategy_weights():
        return {"gap_focus": 0.4, "hot_focus": 0.3, "pair_bias": 0.3}

    monkeypatch.setattr(signal_runner, "_fetch_best_picks", fake_lotto_best)
    monkeypatch.setattr(signal_runner, "_fetch_strategy_weights", fake_lotto_strategy_weights)

    out = await signal_runner.run_signal_check(source="light", curate_result=None, current_draw_no=1101)
    assert "overall_fire" in out
    assert "results" in out
    assert any(r["metric"] == "sim_signal" for r in out["results"])
    # light_check는 confidence 평가 안 함
    assert not any(r["metric"] == "confidence" for r in out["results"])
