"""config_loader.py — shared course-config loading helpers.

Consolidates the nine independent ``load_course_config`` copies that existed
across the plugin (QB2 dedup).

Resolution order (mirrors the original copies):
    1. Explicit ``config_path`` argument (if provided).
    2. ``<PLUGIN_ROOT>/courses/{course_id}/course-config.json``
    3. ``<cwd>/course-config.json``

Public API
----------
load_course_config(course_id=None, config_path=None) -> dict
    Returns the parsed config dict, or ``{}`` when no config is found.
    Matches the semantics of the 7 copies that returned ``dict``:
      audit_bloom_alignment, audit_clo_orphans, audit_mlo_overclaim,
      audit_rubrics, audit_conversion_readiness, scope_triage,
      alignment_skill (which only searched the per-course path, but
      ``{}`` on miss is the same return value).

find_course_config_path(course_id=None, config_path=None) -> Path | None
    Returns the resolved ``Path`` to the config file, or ``None`` when none
    is found.  Call this when the path itself is needed (e.g. audit_mlo_drift
    which returned ``(dict, Path)`` from its bespoke loader).

Deliberately NOT consolidated here
---------------------------------------
``alignment_graph_preview.load_course_config(path: Path) -> dict``
    Takes an already-resolved Path argument — a different interface.
    It is a thin one-liner kept in place; callers pass explicit paths.

``audit_mlo_drift.load_course_config`` / ``course_config_path``
    The drift script needs a ``(dict, Path)`` return plus a
    ``FileNotFoundError`` on miss (different error contract).  Its call sites
    within the same module use the path for reporting.  Rather than forcing
    a divergent exception contract into the shared loader, ``audit_mlo_drift``
    now calls ``find_course_config_path`` + ``load_course_config`` to rebuild
    the ``(dict, Path)`` tuple — see that module for details.
"""

from __future__ import annotations

import json
from pathlib import Path

# PLUGIN_ROOT = parent of the ``scripts/`` directory.
_PLUGIN_ROOT = Path(__file__).resolve().parent.parent


def find_course_config_path(
    course_id: str | None = None,
    config_path: str | Path | None = None,
) -> Path | None:
    """Return the first existing config path, or ``None``.

    Search order:
        1. ``config_path`` (if provided)
        2. ``<PLUGIN_ROOT>/courses/{course_id}/course-config.json``
        3. ``<cwd>/course-config.json``
    """
    candidates: list[Path] = []
    if config_path is not None:
        candidates.append(Path(config_path))
    if course_id is not None:
        candidates.append(_PLUGIN_ROOT / "courses" / str(course_id) / "course-config.json")
    candidates.append(Path.cwd() / "course-config.json")

    for path in candidates:
        if path.exists():
            return path
    return None


def load_course_config(
    course_id: str | None = None,
    config_path: str | Path | None = None,
) -> dict:
    """Load and return the course config as a dict.

    Returns ``{}`` when no config file is found (never raises on missing
    file).  Callers that need to know the resolved path should call
    ``find_course_config_path`` instead.

    Args:
        course_id: Canvas course ID string (used to build the per-course path).
        config_path: Explicit override path to the config JSON file.
    """
    path = find_course_config_path(course_id=course_id, config_path=config_path)
    if path is None:
        return {}
    return json.loads(path.read_text(encoding="utf-8"))
