import importlib
from pathlib import Path

import pytest
import yaml

from tools.errors import AbaqusAgentError, ErrorCode

build_model_module = importlib.import_module("runner.build_model")


def _write_custom_inp_spec(tmp_path, source_inp):
    spec = {
        "meta": {
            "model_name": "custom_model",
            "abaqus_release": "2024",
        },
        "geometry": {
            "type": "custom_inp",
            "inp_path": str(source_inp),
        },
        "material": {
            "name": "Steel",
            "E": 210000.0,
            "nu": 0.3,
        },
        "analysis": {
            "step_type": "Static",
        },
        "outputs": {
            "kpis": [],
        },
    }
    spec_path = tmp_path / "spec.yaml"
    spec_path.write_text(yaml.safe_dump(spec), encoding="utf-8")
    return spec_path


def test_build_model_custom_inp_copies_deck_without_running_cae(tmp_path, monkeypatch):
    source_inp = tmp_path / "source.inp"
    source_inp.write_text("*Heading\ncustom input deck\n", encoding="utf-8")
    spec_path = _write_custom_inp_spec(tmp_path, source_inp)
    workdir = tmp_path / "run"

    def fail_if_called(*args, **kwargs):
        raise AssertionError("custom_inp should not invoke Abaqus CAE noGUI")

    monkeypatch.setattr(build_model_module, "_run_cae_nougui", fail_if_called)

    result = build_model_module.build_model(spec_path, workdir)

    copied_inp = workdir / "custom_model.inp"
    script_path = workdir / "build_model_script.py"
    assert result["cached"] is False
    assert result["inp_path"] == copied_inp
    assert result["cae_path"] == workdir / "custom_model.cae"
    assert copied_inp.read_text(encoding="utf-8") == "*Heading\ncustom input deck\n"
    assert script_path.read_text(encoding="utf-8") == "# custom inp - no CAE script needed\n"


def test_build_model_existing_inp_cache_still_skips_generation(tmp_path, monkeypatch):
    source_inp = tmp_path / "source.inp"
    source_inp.write_text("*Heading\nsource deck\n", encoding="utf-8")
    spec_path = _write_custom_inp_spec(tmp_path, source_inp)
    workdir = tmp_path / "run"
    workdir.mkdir()
    cached_inp = workdir / "custom_model.inp"
    cached_inp.write_text("*Heading\ncached deck\n", encoding="utf-8")

    monkeypatch.setattr(
        build_model_module,
        "_write_cae_script",
        lambda *args, **kwargs: pytest.fail("cached build should not regenerate script"),
    )
    monkeypatch.setattr(
        build_model_module,
        "_run_cae_nougui",
        lambda *args, **kwargs: pytest.fail("cached build should not invoke CAE"),
    )

    result = build_model_module.build_model(spec_path, workdir)

    assert result["cached"] is True
    assert result["inp_path"] == cached_inp
    assert cached_inp.read_text(encoding="utf-8") == "*Heading\ncached deck\n"


def test_build_model_custom_inp_missing_source_raises_structured_error(tmp_path):
    missing_inp = tmp_path / "missing.inp"
    spec_path = _write_custom_inp_spec(tmp_path, missing_inp)

    with pytest.raises(AbaqusAgentError) as exc_info:
        build_model_module.build_model(spec_path, tmp_path / "run")

    assert exc_info.value.code == ErrorCode.FILE_NOT_FOUND
    assert "custom_inp source not found" in str(exc_info.value)
    assert str(missing_inp) in str(exc_info.value)


def test_build_model_generated_script_handoff_with_fake_cae(tmp_path, monkeypatch):
    source_spec = Path(__file__).parent.parent / "cases" / "cantilever" / "spec.yaml"
    spec = yaml.safe_load(source_spec.read_text(encoding="utf-8"))
    spec["meta"]["model_name"] = "fake_cae_model"
    spec_path = tmp_path / "spec.yaml"
    spec_path.write_text(yaml.safe_dump(spec), encoding="utf-8")
    workdir = tmp_path / "run"
    calls = []

    def fake_run_cae(script_path, run_workdir, abaqus_release):
        calls.append((script_path, run_workdir, abaqus_release))
        script_text = script_path.read_text(encoding="utf-8")
        assert "# AUTO-GENERATED by abaqus-agent build_model.py" in script_text
        assert "mdb.jobs['fake_cae_model'].writeInput" in script_text
        assert "mdb.models['fake_cae_model'].ConcentratedForce(" in script_text
        assert "cf2=-1.0" in script_text
        assert "mdb.models['fake_cae_model'].Pressure(" not in script_text
        assert "xMin=10.0/2-0.05" in script_text
        assert "xMin=10.0/2-5.0" not in script_text
        (run_workdir / "fake_cae_model.inp").write_text(
            "*Heading\nfake generated deck\n",
            encoding="utf-8",
        )

    monkeypatch.setattr(build_model_module, "_run_cae_nougui", fake_run_cae)

    result = build_model_module.build_model(spec_path, workdir)

    assert calls == [(workdir / "build_model_script.py", workdir, "2024")]
    assert result["cached"] is False
    assert result["inp_path"] == workdir / "fake_cae_model.inp"
    assert result["inp_path"].read_text(encoding="utf-8") == "*Heading\nfake generated deck\n"
    assert result["cae_path"] == workdir / "fake_cae_model.cae"


def test_build_model_static_pressure_load_stays_supported(tmp_path):
    source_spec = Path(__file__).parent.parent / "cases" / "cantilever" / "spec.yaml"
    spec = yaml.safe_load(source_spec.read_text(encoding="utf-8"))
    spec["meta"]["model_name"] = "pressure_model"
    spec["bc_load"]["load_type"] = "pressure"
    script_path = tmp_path / "build_model_script.py"

    build_model_module._write_cae_script(spec, script_path, tmp_path)

    script_text = script_path.read_text(encoding="utf-8")
    assert "mdb.models['pressure_model'].Pressure(" in script_text
    assert "magnitude=1.0" in script_text
    assert "mdb.models['pressure_model'].ConcentratedForce(" not in script_text
