import importlib.util
import json
import os
import tempfile
import unittest
from unittest import mock
from pathlib import Path


def load_validate_fortran():
    module_path = Path(__file__).resolve().parent / "validate_fortran.py"
    spec = importlib.util.spec_from_file_location("validate_fortran", module_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
    return module


class ValidateFortranTests(unittest.TestCase):
    def test_auto_mode_has_no_commands_when_manifest_is_missing(self):
        validate_fortran = load_validate_fortran()
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            with mock.patch.dict(os.environ, {}, clear=True):
                commands = validate_fortran.discover_commands(root)

        self.assertEqual(commands, [])

    def test_off_mode_skips_manifest(self):
        validate_fortran = load_validate_fortran()
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            manifest = root / "tests" / "fortran" / "manifest.json"
            manifest.parent.mkdir(parents=True)
            manifest.write_text(json.dumps({"tests": []}), encoding="utf-8")
            with mock.patch.dict(os.environ, {"HARNESS_FORTRAN_VALIDATION": "off"}, clear=True):
                commands = validate_fortran.discover_commands(root)

        self.assertEqual(commands, [])

    def test_manifest_requires_available_compiler(self):
        validate_fortran = load_validate_fortran()
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            manifest = root / "tests" / "fortran" / "manifest.json"
            manifest.parent.mkdir(parents=True)
            manifest.write_text(json.dumps({"tests": [{"name": "case", "sources": ["a.f90"]}]}), encoding="utf-8")
            with mock.patch.dict(os.environ, {}, clear=True):
                with mock.patch.object(validate_fortran, "resolve_toolchain", return_value=None):
                    with self.assertRaises(validate_fortran.FortranValidationError):
                        validate_fortran.discover_commands(root)

    def test_manifest_generates_compile_and_run_commands(self):
        validate_fortran = load_validate_fortran()
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            manifest = root / "tests" / "fortran" / "manifest.json"
            manifest.parent.mkdir(parents=True)
            manifest.write_text(
                json.dumps(
                    {
                        "tests": [
                            {
                                "name": "umat_linear_elastic_kernel",
                                "sources": [
                                    "src/fortran/kernels/linear_elastic.f90",
                                    "tests/fortran/test_umat_linear_elastic.f90",
                                ],
                            }
                        ]
                    }
                ),
                encoding="utf-8",
            )
            toolchain = validate_fortran.FortranToolchain(name="ifx", executable="ifx", env_script=None)
            with mock.patch.dict(os.environ, {}, clear=True):
                with mock.patch.object(validate_fortran, "resolve_toolchain", return_value=toolchain):
                    commands = validate_fortran.discover_commands(root)

        self.assertEqual(len(commands), 2)
        self.assertIn("ifx /nologo", commands[0])
        self.assertIn("src\\fortran\\kernels\\linear_elastic.f90", commands[0])
        self.assertIn("/exe:", commands[0])
        self.assertTrue(commands[1].endswith("umat_linear_elastic_kernel.exe"))

    def test_manifest_build_commands_create_test_build_directories(self):
        validate_fortran = load_validate_fortran()
        with tempfile.TemporaryDirectory() as tmp:
            root = Path(tmp)
            manifest = root / "tests" / "fortran" / "manifest.json"
            manifest.parent.mkdir(parents=True)
            manifest.write_text(
                json.dumps(
                    {
                        "tests": [
                            {
                                "name": "sample_kernel_stiffness",
                                "sources": [
                                    "src/fortran/sample_kernel.f90",
                                    "tests/fortran/test_sample_kernel.f90",
                                ],
                            }
                        ]
                    }
                ),
                encoding="utf-8",
            )
            toolchain = validate_fortran.FortranToolchain(name="ifx", executable="ifx", env_script=None)
            with mock.patch.dict(os.environ, {}, clear=True):
                with mock.patch.object(validate_fortran, "resolve_toolchain", return_value=toolchain):
                    validate_fortran.discover_commands(root)

            build_dir = root / "build" / "fortran-tests" / "sample_kernel_stiffness"
            self.assertTrue(build_dir.is_dir())


if __name__ == "__main__":
    unittest.main()
