"""Tests for scripts/config_loader.py — shared course-config loading helpers.

Characterises the fallback resolution logic so future refactors cannot
silently change which config file gets loaded.
"""

import json
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))

from config_loader import find_course_config_path, load_course_config


# ---------------------------------------------------------------------------
# Fixtures / helpers
# ---------------------------------------------------------------------------


def _write_config(path: Path, data: dict) -> Path:
    """Write *data* as JSON to *path*, creating parent dirs."""
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data), encoding="utf-8")
    return path


# ---------------------------------------------------------------------------
# find_course_config_path
# ---------------------------------------------------------------------------


class TestFindCourseConfigPath:
    def test_returns_none_when_nothing_exists(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        assert find_course_config_path(course_id="99999") is None

    def test_explicit_config_path_takes_priority(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        explicit = tmp_path / "explicit-config.json"
        _write_config(explicit, {"source": "explicit"})
        # Also create course-dir config to make sure explicit wins
        course_cfg = tmp_path / "courses" / "123" / "course-config.json"
        _write_config(course_cfg, {"source": "course-dir"})
        result = find_course_config_path(course_id="123", config_path=str(explicit))
        assert result == explicit

    def test_course_dir_config_before_cwd_fallback(self, tmp_path, monkeypatch):
        # Redirect PLUGIN_ROOT-relative lookup by placing files under tmp_path
        # We do this by monkeypatching the module-level _PLUGIN_ROOT constant.
        import config_loader as cl
        monkeypatch.setattr(cl, "_PLUGIN_ROOT", tmp_path)
        monkeypatch.chdir(tmp_path)

        # Create both paths; course-dir should win
        course_cfg = tmp_path / "courses" / "42" / "course-config.json"
        _write_config(course_cfg, {"source": "course-dir"})
        cwd_cfg = tmp_path / "course-config.json"
        _write_config(cwd_cfg, {"source": "cwd"})

        result = find_course_config_path(course_id="42")
        assert result == course_cfg

    def test_cwd_fallback_used_when_no_course_dir(self, tmp_path, monkeypatch):
        import config_loader as cl
        monkeypatch.setattr(cl, "_PLUGIN_ROOT", tmp_path)
        monkeypatch.chdir(tmp_path)

        cwd_cfg = tmp_path / "course-config.json"
        _write_config(cwd_cfg, {"source": "cwd"})

        result = find_course_config_path(course_id="999")
        assert result == cwd_cfg

    def test_no_course_id_still_checks_cwd(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        cwd_cfg = tmp_path / "course-config.json"
        _write_config(cwd_cfg, {"source": "cwd"})

        result = find_course_config_path()
        assert result == cwd_cfg

    def test_returns_path_object(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        cwd_cfg = tmp_path / "course-config.json"
        _write_config(cwd_cfg, {})
        result = find_course_config_path()
        assert isinstance(result, Path)


# ---------------------------------------------------------------------------
# load_course_config
# ---------------------------------------------------------------------------


class TestLoadCourseConfig:
    def test_returns_empty_dict_when_no_file(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        assert load_course_config(course_id="9999") == {}

    def test_returns_parsed_json(self, tmp_path, monkeypatch):
        import config_loader as cl
        monkeypatch.setattr(cl, "_PLUGIN_ROOT", tmp_path)
        monkeypatch.chdir(tmp_path)

        course_cfg = tmp_path / "courses" / "7" / "course-config.json"
        data = {"course": {"id": "7", "name": "Test Course"}, "clos": []}
        _write_config(course_cfg, data)

        result = load_course_config(course_id="7")
        assert result == data

    def test_explicit_path_wins(self, tmp_path, monkeypatch):
        import config_loader as cl
        monkeypatch.setattr(cl, "_PLUGIN_ROOT", tmp_path)
        monkeypatch.chdir(tmp_path)

        explicit = tmp_path / "override.json"
        _write_config(explicit, {"source": "explicit"})
        course_cfg = tmp_path / "courses" / "7" / "course-config.json"
        _write_config(course_cfg, {"source": "course-dir"})

        result = load_course_config(course_id="7", config_path=str(explicit))
        assert result["source"] == "explicit"

    def test_cwd_fallback(self, tmp_path, monkeypatch):
        import config_loader as cl
        monkeypatch.setattr(cl, "_PLUGIN_ROOT", tmp_path)
        monkeypatch.chdir(tmp_path)

        cwd_cfg = tmp_path / "course-config.json"
        _write_config(cwd_cfg, {"source": "cwd"})

        result = load_course_config(course_id="999")
        assert result["source"] == "cwd"

    def test_none_course_id_returns_empty_or_cwd(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        # No cwd config → empty
        assert load_course_config(course_id=None) == {}

    def test_missing_file_returns_empty_not_raise(self, tmp_path, monkeypatch):
        monkeypatch.chdir(tmp_path)
        # Should never raise FileNotFoundError
        result = load_course_config(course_id="doesnotexist")
        assert result == {}

    def test_returns_dict(self, tmp_path, monkeypatch):
        import config_loader as cl
        monkeypatch.setattr(cl, "_PLUGIN_ROOT", tmp_path)
        monkeypatch.chdir(tmp_path)
        cfg = tmp_path / "courses" / "1" / "course-config.json"
        _write_config(cfg, {"key": "val"})
        assert isinstance(load_course_config(course_id="1"), dict)
