# -*- coding: utf-8 -*-
"""tests.regression.test_collector_context_mismatch — task-2636.

Spec: memory/specs/system_callback_collector_canonical_root_spec_260523.md §6, §8.
"""
from __future__ import annotations

import json
from pathlib import Path

import pytest

from utils.canonical_root_resolver import (
    MISMATCH_DECISION,
    MISMATCH_EVENT_LOG_REL,
    MISMATCH_EVENT_SCHEMA,
    detect_context_mismatch,
)


def _read_events(log_root: Path):
    log_path = log_root / MISMATCH_EVENT_LOG_REL
    if not log_path.exists():
        return []
    return [
        json.loads(line)
        for line in log_path.read_text(encoding="utf-8").splitlines()
        if line.strip()
    ]


def test_cwd_equals_canonical_no_event(tmp_path: Path):
    env = {"task_id": "task-2636-tm-1", "canonical_root": str(tmp_path)}
    out = detect_context_mismatch(env, current_cwd=str(tmp_path), log_root=str(tmp_path))
    assert out["mismatch"] is False
    assert out["recorded_event_id"] is None
    assert out["decision"] == MISMATCH_DECISION
    assert _read_events(tmp_path) == []


def test_cwd_differs_canonical_records_event(tmp_path: Path):
    env = {
        "task_id": "task-2636-tm-2",
        "canonical_root": str(tmp_path / "canonical"),
        "result_path": "memory/x.json",
    }
    (tmp_path / "canonical").mkdir()
    other_cwd = tmp_path / "autoset"
    other_cwd.mkdir()
    out = detect_context_mismatch(
        env, current_cwd=str(other_cwd), log_root=str(tmp_path)
    )
    assert out["mismatch"] is True
    assert out["recorded_event_id"] is not None
    assert out["decision"] == MISMATCH_DECISION
    events = _read_events(tmp_path)
    assert len(events) == 1
    rec = events[0]
    assert rec["schema"] == MISMATCH_EVENT_SCHEMA
    assert rec["task_id"] == "task-2636-tm-2"
    assert rec["cwd"] == str(other_cwd)
    assert rec["canonical_root_resolved"] == str(tmp_path / "canonical")
    assert rec["canonical_root_envelope"] == str(tmp_path / "canonical")
    assert rec["decision"] == MISMATCH_DECISION
    assert rec["event_id"] == out["recorded_event_id"]
    assert any("memory/x.json" in p for p in rec["delta_paths"])


def test_mismatch_event_schema_validator(tmp_path: Path):
    env = {"task_id": "t", "canonical_root": "/a"}
    detect_context_mismatch(env, current_cwd="/b", log_root=str(tmp_path))
    events = _read_events(tmp_path)
    assert events, "expected one event"
    rec = events[0]
    required_keys = {
        "schema",
        "event_id",
        "ts_kst",
        "task_id",
        "cwd",
        "canonical_root_envelope",
        "canonical_root_resolved",
        "delta_paths",
        "decision",
    }
    assert required_keys.issubset(rec.keys())
    assert rec["schema"] == MISMATCH_EVENT_SCHEMA


def test_mismatch_append_only(tmp_path: Path):
    env = {"task_id": "t", "canonical_root": "/a"}
    for cwd in ("/b", "/c", "/d"):
        detect_context_mismatch(env, current_cwd=cwd, log_root=str(tmp_path))
    events = _read_events(tmp_path)
    assert len(events) == 3
    assert {e["cwd"] for e in events} == {"/b", "/c", "/d"}


def test_detect_can_skip_event_write(tmp_path: Path):
    env = {"task_id": "t", "canonical_root": "/a"}
    out = detect_context_mismatch(
        env, current_cwd="/b", log_root=str(tmp_path), write_event=False
    )
    assert out["mismatch"] is True
    assert out["recorded_event_id"] is None
    assert _read_events(tmp_path) == []


def test_decision_is_always_canonical(tmp_path: Path):
    # cwd never wins — the contract is that the resolver always proceeds with
    # canonical_root, regardless of how far cwd diverges (spec ANCHOR-2).
    env = {"task_id": "t", "canonical_root": "/canon"}
    out = detect_context_mismatch(
        env, current_cwd="/totally/different", log_root=str(tmp_path)
    )
    assert out["decision"] == "PROCEED_WITH_CANONICAL_ROOT"
    assert out["canonical_root"] == "/canon"


def test_envelope_canonical_root_explicit_persisted(tmp_path: Path):
    env = {"task_id": "t", "canonical_root": "/explicit"}
    detect_context_mismatch(env, current_cwd="/other", log_root=str(tmp_path))
    rec = _read_events(tmp_path)[0]
    assert rec["canonical_root_envelope"] == "/explicit"


def test_envelope_canonical_root_missing_recorded_as_null(tmp_path: Path):
    env = {"task_id": "t"}  # no canonical_root
    detect_context_mismatch(env, current_cwd="/other", log_root=str(tmp_path))
    rec = _read_events(tmp_path)[0]
    assert rec["canonical_root_envelope"] is None
    assert rec["canonical_root_resolved"] == "/home/jay/workspace"


@pytest.mark.parametrize(
    "scenario,expects_mismatch",
    [
        ("canonical_root_explicit", False),
        ("canonical_root_missing_default", False),
        ("canonical_root_wrong_absolute", True),
        ("cwd_in_autoset_canonical_in_workspace", True),
        ("sendfile_only_not_trigger", False),
        ("normal_callback_is_trigger", False),
    ],
)
def test_fixture_mismatch_decision(tmp_path: Path, scenario, expects_mismatch):
    fx = (
        Path(__file__).resolve().parent.parent
        / "fixtures"
        / "callback_collector_canonical_root"
        / scenario
    )
    evidence = json.loads((fx / "evidence.json").read_text(encoding="utf-8"))
    expected = json.loads((fx / "expected.json").read_text(encoding="utf-8"))
    out = detect_context_mismatch(
        evidence["envelope"],
        current_cwd=evidence["current_cwd"],
        log_root=str(tmp_path),
    )
    assert out["mismatch"] is expects_mismatch
    assert out["mismatch"] is expected["context_mismatch"]
    events = _read_events(tmp_path)
    assert bool(events) is expected["mismatch_event_recorded"]
