#!/usr/bin/env python3
"""Run no-Abaqus Intel Fortran manifest tests."""

from __future__ import annotations

import json
import os
import subprocess
import sys
from pathlib import Path

try:
    from fortran_toolchain import FortranToolchain, resolve_toolchain, wrap_command
except ModuleNotFoundError:
    from scripts.fortran_toolchain import FortranToolchain, resolve_toolchain, wrap_command


MANIFEST_PATH = Path("tests/fortran/manifest.json")
BUILD_ROOT = Path("build/fortran-tests")
VALIDATION_MODES = {"off", "detect", "auto", "compile"}


class FortranValidationError(RuntimeError):
    pass


def validation_mode(env: dict[str, str] | None = None) -> str:
    env = env or os.environ
    mode = env.get("HARNESS_FORTRAN_VALIDATION", "auto").lower()
    if mode not in VALIDATION_MODES:
        raise FortranValidationError(f"Unsupported HARNESS_FORTRAN_VALIDATION: {mode}")
    return mode


def load_manifest(root: Path) -> dict | None:
    manifest = root / MANIFEST_PATH
    if not manifest.exists():
        return None
    try:
        payload = json.loads(manifest.read_text(encoding="utf-8"))
    except json.JSONDecodeError as exc:
        raise FortranValidationError(f"Invalid Fortran manifest JSON: {manifest}: {exc}") from exc
    if not isinstance(payload, dict) or not isinstance(payload.get("tests", []), list):
        raise FortranValidationError(f"Fortran manifest must contain a tests array: {manifest}")
    return payload


def _validate_test_record(record: dict) -> tuple[str, list[str]]:
    if not isinstance(record, dict):
        raise FortranValidationError("Fortran manifest test record must be an object.")
    name = record.get("name")
    sources = record.get("sources")
    if not isinstance(name, str) or not name:
        raise FortranValidationError("Fortran manifest test record is missing name.")
    if not isinstance(sources, list) or not all(isinstance(source, str) and source for source in sources):
        raise FortranValidationError(f"Fortran manifest test {name} must define source paths.")
    return name, sources


def build_test_commands(root: Path, manifest: dict, toolchain: FortranToolchain) -> list[str]:
    commands: list[str] = []
    for record in manifest.get("tests", []):
        name, sources = _validate_test_record(record)
        build_dir = root / BUILD_ROOT / name
        build_dir.mkdir(parents=True, exist_ok=True)
        exe_path = build_dir / f"{name}.exe"
        source_paths = [root / source for source in sources]
        compile_args: list[str | Path] = [
            toolchain.executable,
            "/nologo",
            *source_paths,
            f"/exe:{exe_path}",
        ]
        commands.append(wrap_command(toolchain, compile_args))
        commands.append(str(exe_path))
    return commands


def discover_commands(root: Path, env: dict[str, str] | None = None) -> list[str]:
    env = env or os.environ
    mode = validation_mode(env)
    if mode == "off":
        return []

    manifest = load_manifest(root)
    if manifest is None:
        return []

    toolchain = resolve_toolchain(env)
    if toolchain is None:
        raise FortranValidationError("Fortran validation manifest exists, but no Intel oneAPI Fortran compiler was found.")

    if mode == "detect":
        return []
    return build_test_commands(root, manifest, toolchain)


def run_command(command: str, root: Path) -> subprocess.CompletedProcess:
    return subprocess.run(command, cwd=root, shell=True, capture_output=True, text=True, encoding="utf-8", errors="replace")


def main() -> int:
    root = Path(__file__).resolve().parent.parent
    try:
        commands = discover_commands(root)
    except FortranValidationError as exc:
        print(f"Fortran validation failed: {exc}", file=sys.stderr)
        return 2

    if not commands:
        print("No Fortran validation commands configured.")
        return 0

    for command in commands:
        print(f"$ {command}")
        result = run_command(command, root)
        if result.stdout.strip():
            print("[stdout]")
            print(result.stdout.strip())
        if result.stderr.strip():
            print("[stderr]", file=sys.stderr)
            print(result.stderr.strip(), file=sys.stderr)
        if result.returncode != 0:
            print(f"Fortran validation failed: {command}", file=sys.stderr)
            return result.returncode

    print("Fortran validation succeeded.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
