from __future__ import annotations

import hashlib
import json
import os
import tempfile
from pathlib import Path
from typing import Any

PACKAGE_ROOT = Path(__file__).resolve().parents[1]
CONFIG_PATH = PACKAGE_ROOT / "config" / "gal_fingerprint_i_registered_specification.json"
BUNDLED_DATA = PACKAGE_ROOT / "data" / "registered"
CHAMBER_VERSION = "0.2.2"
RUNTIME_TOKEN = "GFI022_RUNTIME"


def sha256_path(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as handle:
        for block in iter(lambda: handle.read(1024 * 1024), b""):
            digest.update(block)
    return digest.hexdigest()


def load_config(path: Path | None = None) -> dict[str, Any]:
    selected = path or CONFIG_PATH
    return json.loads(selected.read_text(encoding="utf-8"))


def discover_project_root() -> Path | None:
    """Find the containing UNNS galaxy project when the chamber is installed in it."""
    for candidate in [PACKAGE_ROOT, *PACKAGE_ROOT.parents]:
        if (
            (candidate / "inputs/sample_lists/primary_121.txt").is_file()
            and (candidate / "data/prepared/baryonic_profiles.csv").is_file()
            and (candidate / "outputs/exports/struc_i").is_dir()
        ):
            return candidate
    return None


def _project_key(project: Path | None) -> str:
    source = str(project.resolve() if project else PACKAGE_ROOT.resolve()).lower()
    return hashlib.sha256(source.encode("utf-8")).hexdigest()[:12]


def _windows_runtime_root(project: Path | None) -> Path:
    override = os.environ.get("GFI_RUNTIME_ROOT", "").strip()
    if override:
        base = Path(override).expanduser()
    else:
        base = Path(os.environ.get("LOCALAPPDATA") or tempfile.gettempdir()) / "UNNS" / RUNTIME_TOKEN
    return base / _project_key(project)


def active_roots() -> dict[str, Path | str | bool]:
    project = discover_project_root()
    installed = project is not None
    runtime_override = os.environ.get("GFI_RUNTIME_ROOT", "").strip()
    if runtime_override:
        run_root = Path(runtime_override).expanduser() / _project_key(project)
        storage_mode = "WINDOWS_SHORT_RUNTIME" if os.name == "nt" else "EXTERNAL_SHORT_RUNTIME"
    elif os.name == "nt":
        run_root = _windows_runtime_root(project)
        storage_mode = "WINDOWS_SHORT_RUNTIME"
    elif installed:
        run_root = project / "runs/gal_fingerprint_i/chamber_runs/gate_c"
        storage_mode = "PROJECT_RUNTIME"
    else:
        run_root = PACKAGE_ROOT / "outputs/runs/gate_c"
        storage_mode = "STANDALONE_RUNTIME"

    if installed:
        assert project is not None
        canonical_run_root = project / "runs/gal_fingerprint_i/chamber_runs/gate_c"
        analysis_root = project / "analysis/gal_fingerprint_i/outputs"
        export_root = project / "outputs/exports/struc_i"
        data_root: Path = project
    else:
        canonical_run_root = PACKAGE_ROOT / "outputs/runs/gate_c"
        analysis_root = PACKAGE_ROOT / "outputs/analysis"
        export_root = PACKAGE_ROOT / "outputs/exports/struc_i"
        data_root = BUNDLED_DATA

    return {
        "installed_in_project": installed,
        "project_root": project or "",
        "data_root": data_root,
        "run_root": run_root,
        "canonical_run_root": canonical_run_root,
        "analysis_root": analysis_root,
        "export_root": export_root,
        "runtime_storage_mode": storage_mode,
    }


def registered_paths() -> dict[str, Path]:
    roots = active_roots()
    if roots["installed_in_project"]:
        project = Path(roots["project_root"])
        return {
            "primary": project / "inputs/sample_lists/primary_121.txt",
            "ladder_manifest": project / "inputs/ladders/ladder_manifest.csv",
            "ladders": project / "inputs/ladders/struc_i",
            "profiles": project / "data/prepared/baryonic_profiles.csv",
            "r1_profiles": project / "runs/consolidated/struc_i_primary_121_profiles.csv",
            "gate_b_result": project / "analysis/gal_fingerprint_i/outputs/reports/GAL_FINGERPRINT_I_GATE_B_RESULT_RECORD.json",
            "gate_b_report": project / "analysis/gal_fingerprint_i/outputs/reports/GAL_FINGERPRINT_I_GATE_B_REPORT.md",
        }
    return {
        "primary": BUNDLED_DATA / "primary_121.txt",
        "ladder_manifest": BUNDLED_DATA / "ladder_manifest.csv",
        "ladders": BUNDLED_DATA / "ladders",
        "profiles": BUNDLED_DATA / "baryonic_profiles.csv",
        "r1_profiles": BUNDLED_DATA / "r1_struc_i_primary_121_profiles.csv",
        "gate_b_result": BUNDLED_DATA / "gate_b" / "GAL_FINGERPRINT_I_GATE_B_RESULT_RECORD.json",
        "gate_b_report": BUNDLED_DATA / "gate_b" / "GAL_FINGERPRINT_I_GATE_B_REPORT.md",
    }


def ensure_runtime_dirs() -> None:
    roots = active_roots()
    Path(roots["run_root"]).mkdir(parents=True, exist_ok=True)
    Path(roots["analysis_root"]).mkdir(parents=True, exist_ok=True)
    Path(roots["export_root"]).mkdir(parents=True, exist_ok=True)
    canonical = Path(roots["canonical_run_root"])
    if canonical != Path(roots["run_root"]):
        try:
            canonical.mkdir(parents=True, exist_ok=True)
            pointer = {
                "chamber_version": CHAMBER_VERSION,
                "gate": "GATE_C",
                "runtime_storage_mode": roots["runtime_storage_mode"],
                "active_run_root": str(Path(roots["run_root"]).resolve()),
            }
            (canonical / "ACTIVE_RUNTIME_GFI022.json").write_text(
                json.dumps(pointer, indent=2), encoding="utf-8"
            )
        except OSError:
            pass


def runtime_storage_probe() -> dict[str, Any]:
    """Verify the exact atomic-write pattern used by scientific checkpoints."""
    ensure_runtime_dirs()
    root = Path(active_roots()["run_root"])
    probe_dir = root / "_storage_probe"
    probe_dir.mkdir(parents=True, exist_ok=True)
    final = probe_dir / "probe.json"
    temp = probe_dir / "probe.json.tmp"
    try:
        temp.write_text('{"probe": true}', encoding="utf-8")
        os.replace(temp, final)
        content_ok = final.read_text(encoding="utf-8") == '{"probe": true}'
        return {
            "runtime_write_test_pass": bool(content_ok),
            "runtime_storage_mode": active_roots()["runtime_storage_mode"],
            "runtime_root": str(root.resolve()),
            "runtime_root_length": len(str(root.resolve())),
        }
    except OSError as exc:
        return {
            "runtime_write_test_pass": False,
            "runtime_storage_mode": active_roots()["runtime_storage_mode"],
            "runtime_root": str(root),
            "runtime_root_length": len(str(root)),
            "runtime_write_error": f"{type(exc).__name__}: {exc}",
        }
    finally:
        for candidate in (temp, final):
            try:
                candidate.unlink(missing_ok=True)
            except OSError:
                pass
        try:
            probe_dir.rmdir()
        except OSError:
            pass
