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

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

import json
from pathlib import Path

import pytest

from utils.canonical_root_resolver import (
    CANONICAL_ROOT_DEFAULT,
    CLASS_FOUND,
    CLASS_MISSING_PATH_FIELD,
    CLASS_NOT_FOUND,
    find_artifact,
    resolve_canonical_root,
    resolve_path,
)


FIXTURE_ROOT = (
    Path(__file__).resolve().parent.parent
    / "fixtures"
    / "callback_collector_canonical_root"
)

SCENARIOS = [
    "canonical_root_explicit",
    "canonical_root_missing_default",
    "canonical_root_wrong_absolute",
    "relative_paths_resolve_correctly",
    "absolute_paths_passthrough",
    "cwd_in_autoset_canonical_in_workspace",
    "sendfile_only_not_trigger",
    "normal_callback_is_trigger",
]


def _load_fixture(scenario: str):
    base = FIXTURE_ROOT / scenario
    evidence = json.loads((base / "evidence.json").read_text(encoding="utf-8"))
    expected = json.loads((base / "expected.json").read_text(encoding="utf-8"))
    return evidence, expected


def test_canonical_root_default_constant():
    assert CANONICAL_ROOT_DEFAULT == "/home/jay/workspace"


def test_resolve_canonical_root_envelope_override():
    env = {"canonical_root": "/explicit/root"}
    assert resolve_canonical_root(env) == "/explicit/root"


def test_resolve_canonical_root_default_when_missing():
    assert resolve_canonical_root({}) == CANONICAL_ROOT_DEFAULT
    assert resolve_canonical_root({"canonical_root": None}) == CANONICAL_ROOT_DEFAULT
    assert resolve_canonical_root({"canonical_root": ""}) == CANONICAL_ROOT_DEFAULT


def test_resolve_canonical_root_rejects_relative_envelope():
    with pytest.raises(ValueError, match="absolute"):
        resolve_canonical_root({"canonical_root": "relative/path"})


def test_resolve_canonical_root_rejects_relative_default():
    with pytest.raises(ValueError, match="absolute"):
        resolve_canonical_root({}, default="relative/path")


def test_resolve_canonical_root_non_dict_envelope():
    assert resolve_canonical_root(None) == CANONICAL_ROOT_DEFAULT
    assert resolve_canonical_root("string") == CANONICAL_ROOT_DEFAULT


def test_resolve_path_absolute_passthrough():
    env = {"canonical_root": "/home/jay/workspace", "result_path": "/tmp/x.json"}
    assert resolve_path(env, "result_path") == "/tmp/x.json"


def test_resolve_path_relative_concat():
    env = {"canonical_root": "/root", "report_path": "memory/reports/t.md"}
    assert resolve_path(env, "report_path") == "/root/memory/reports/t.md"


def test_resolve_path_missing_returns_empty():
    env = {"canonical_root": "/root"}
    assert resolve_path(env, "result_path") == ""
    assert resolve_path({"canonical_root": "/root", "report_path": ""}, "report_path") == ""


def test_resolve_path_explicit_root_overrides_envelope():
    env = {"canonical_root": "/from/envelope", "result_path": "x.json"}
    assert resolve_path(env, "result_path", canonical_root="/other") == "/other/x.json"


def test_resolve_path_rejects_relative_canonical_root():
    env = {"result_path": "x.json"}
    with pytest.raises(ValueError, match="absolute"):
        resolve_path(env, "result_path", canonical_root="relative")


@pytest.mark.parametrize("scenario", SCENARIOS)
def test_fixture_canonical_root_resolution(scenario):
    evidence, expected = _load_fixture(scenario)
    envelope = evidence["envelope"]
    canonical = resolve_canonical_root(envelope)
    assert canonical == expected["canonical_root"], (
        f"scenario={scenario} canonical mismatch"
    )

    result_path = resolve_path(envelope, "result_path", canonical_root=canonical)
    report_path = resolve_path(envelope, "report_path", canonical_root=canonical)
    assert result_path == expected["result_path_resolved"], (
        f"scenario={scenario} result_path resolve mismatch"
    )
    assert report_path == expected["report_path_resolved"], (
        f"scenario={scenario} report_path resolve mismatch"
    )


def test_find_artifact_missing_path_field():
    out = find_artifact({}, "result_path")
    assert out["found"] is False
    assert out["classification"] == CLASS_MISSING_PATH_FIELD
    assert out["lookup_attempts"] == []


def test_find_artifact_found_via_canonical(tmp_path: Path):
    target = tmp_path / "reports" / "x.json"
    target.parent.mkdir(parents=True)
    target.write_text("{}")
    env = {
        "canonical_root": str(tmp_path),
        "result_path": "reports/x.json",
    }
    out = find_artifact(env, "result_path")
    assert out["found"] is True
    assert out["classification"] == CLASS_FOUND
    assert out["resolved_path"] == str(target)


def test_find_artifact_not_found(tmp_path: Path):
    env = {
        "canonical_root": str(tmp_path),
        "result_path": "missing/nope.json",
    }
    out = find_artifact(env, "result_path")
    assert out["found"] is False
    assert out["classification"] == CLASS_NOT_FOUND
    assert len(out["lookup_attempts"]) >= 1


def test_find_artifact_fallback_to_default(tmp_path: Path, monkeypatch):
    # primary canonical_root is empty; the spec fallback at CANONICAL_ROOT_DEFAULT
    # must be tried next when the relative path lives there.
    fake_default = tmp_path / "default_root"
    target = fake_default / "memory" / "reports" / "x.json"
    target.parent.mkdir(parents=True)
    target.write_text("{}")
    # primary root is a different empty directory
    primary_root = tmp_path / "primary_root"
    primary_root.mkdir()
    monkeypatch.setattr(
        "utils.canonical_root_resolver.CANONICAL_ROOT_DEFAULT", str(fake_default)
    )
    env = {
        "canonical_root": str(primary_root),
        "result_path": "memory/reports/x.json",
    }
    out = find_artifact(env, "result_path")
    assert out["found"] is True
    assert out["resolved_path"] == str(target)
    assert len(out["lookup_attempts"]) == 2
