"""Tests for the abaqus-agent command line interface."""

from __future__ import annotations

import json

import yaml

import cli


def test_no_args_keeps_server_default(monkeypatch):
    called = {}

    def fake_serve(_args):
        called["serve"] = True
        return 0

    monkeypatch.setattr(cli, "_cmd_serve", fake_serve)
    assert cli.main([]) == 0
    assert called["serve"] is True


def test_capsule_init_cli(tmp_path, capsys):
    inp = tmp_path / "model.inp"
    inp.write_text("*Heading\nmodel\n", encoding="utf-8")
    out = tmp_path / "capsule"

    rc = cli.main(["capsule", "init", "--from-inp", str(inp), "--out", str(out)])

    captured = capsys.readouterr()
    data = json.loads(captured.out)
    assert rc == 0
    assert data["inputs"]["model_name"] == "model"
    assert (out / "capsule.json").exists()


def test_validate_env_cli_prints_json(monkeypatch, capsys):
    def fake_preflight(**_kwargs):
        assert _kwargs["expected_release"] == "2026"
        assert _kwargs["workdir"] == "runs"
        assert _kwargs["runner_cfg"] == {"cpus": 2}
        return {
            "status": "ready",
            "platform": {},
            "abaqus": {"release_check": {}},
            "checks": [],
        }

    monkeypatch.setattr(cli, "run_environment_preflight", fake_preflight)

    rc = cli.main([
        "validate", "env",
        "--expected-release", "2026",
        "--workdir", "runs",
        "--runner-json", '{"cpus": 2}',
        "--json",
    ])

    data = json.loads(capsys.readouterr().out)
    assert rc == 0
    assert data["status"] == "ready"


def test_validate_env_cli_strict_fails_when_not_ready(monkeypatch):
    def fake_preflight(**_kwargs):
        return {
            "status": "missing_abaqus",
            "platform": {},
            "abaqus": {"release_check": {}},
            "checks": [],
        }

    monkeypatch.setattr(cli, "run_environment_preflight", fake_preflight)

    assert cli.main(["validate", "env", "--strict", "--json"]) == 1


def test_validate_record_cli_dry_run(capsys):
    rc = cli.main([
        "validate", "record",
        "--date", "2026-06-02",
        "--environment", "macOS",
        "--abaqus", "Not installed",
        "--workflow", "dry-run",
        "--result", "PASS",
        "--evidence", "5 / 5",
        "--dry-run",
    ])

    out = capsys.readouterr().out
    assert rc == 0
    assert "| 2026-06-02 | macOS | Not installed | dry-run | PASS | 5 / 5 |" in out


def test_validate_record_cli_appends_matrix(tmp_path, capsys):
    matrix = tmp_path / "VALIDATION_MATRIX.md"
    matrix.write_text(
        "# Validation Matrix\n\n"
        "## Current Evidence\n\n"
        "| Date | Environment | Abaqus | Case / Workflow | Result | Evidence |\n"
        "|---|---|---|---|---|---|\n\n"
        "## Public Case Coverage\n",
        encoding="utf-8",
    )

    rc = cli.main([
        "validate", "record",
        "--matrix", str(matrix),
        "--date", "2026-06-02",
        "--environment", "Windows",
        "--abaqus", "Abaqus 2021",
        "--workflow", "cantilever",
        "--result", "PASS",
        "--evidence", "status=COMPLETED",
    ])

    assert rc == 0
    assert "Appended validation evidence" in capsys.readouterr().out
    assert "| 2026-06-02 | Windows | Abaqus 2021 | cantilever | PASS | status=COMPLETED |" in matrix.read_text(encoding="utf-8")


def test_memory_search_cli_passes_facet_filters(monkeypatch, capsys):
    def fake_search(query):
        assert query.geometry_type == "custom"
        assert query.solver == "standard"
        assert query.material_name == "Steel"
        return {
            "total_indexed": 1,
            "total_matches": 0,
            "facets": {},
            "query": {},
            "matches": [],
        }

    monkeypatch.setattr(cli, "search_case_memory", fake_search)

    rc = cli.main([
        "memory", "search", "runs",
        "--geometry-type", "custom",
        "--solver", "standard",
        "--material-name", "Steel",
        "--json",
    ])

    assert rc == 0
    assert json.loads(capsys.readouterr().out)["total_indexed"] == 1


def test_report_export_cli_writes_json(monkeypatch, tmp_path, capsys):
    out = tmp_path / "report.md"

    def fake_export(source, out_path, **kwargs):
        assert source == "runs/demo"
        assert out_path == str(out)
        assert kwargs["export_format"] == "md"
        assert kwargs["template"] == "client_summary"
        return {"format": "md", "output_path": str(out), "bytes": 123}

    monkeypatch.setattr(cli, "export_offline_run_report", fake_export)

    rc = cli.main([
        "report", "export", "runs/demo",
        "--out", str(out),
        "--format", "md",
        "--template", "client_summary",
        "--json",
    ])

    data = json.loads(capsys.readouterr().out)
    assert rc == 0
    assert data["format"] == "md"
    assert data["bytes"] == 123


def test_report_export_cli_accepts_pdf_format(monkeypatch, tmp_path, capsys):
    out = tmp_path / "report.pdf"

    def fake_export(source, out_path, **kwargs):
        assert source == "runs/demo"
        assert out_path == str(out)
        assert kwargs["export_format"] == "pdf"
        return {"format": "pdf", "output_path": str(out), "bytes": 456}

    monkeypatch.setattr(cli, "export_offline_run_report", fake_export)

    rc = cli.main([
        "report", "export", "runs/demo",
        "--out", str(out),
        "--format", "pdf",
        "--json",
    ])

    data = json.loads(capsys.readouterr().out)
    assert rc == 0
    assert data["format"] == "pdf"
    assert data["bytes"] == 456


def test_report_export_cli_accepts_engineering_delivery_template(monkeypatch, tmp_path):
    out = tmp_path / "delivery.html"

    def fake_export(source, out_path, **kwargs):
        assert source == "runs/demo"
        assert out_path == str(out)
        assert kwargs["template"] == "engineering_delivery"
        return {"format": "html", "output_path": str(out), "bytes": 789}

    monkeypatch.setattr(cli, "export_offline_run_report", fake_export)

    assert cli.main([
        "report", "export", "runs/demo",
        "--out", str(out),
        "--template", "engineering_delivery",
    ]) == 0


def test_doctor_cli_directory_mode(tmp_path, capsys):
    (tmp_path / "job.msg").write_text("Too many attempts made for this increment\n", encoding="utf-8")

    rc = cli.main(["doctor", str(tmp_path)])

    out = capsys.readouterr().out
    assert rc == 0
    assert "too_many_attempts" in out


def test_doctor_cli_writes_expanded_pattern_report(tmp_path):
    msg = tmp_path / "job.msg"
    report = tmp_path / "doctor.md"
    msg.write_text("Node set FIXED_NODES has not been defined\n", encoding="utf-8")

    rc = cli.main(["doctor", str(msg), "--out", str(report)])

    text = report.read_text(encoding="utf-8")
    assert rc == 0
    assert "Solver Doctor Report" in text
    assert "missing_node_set" in text


def test_doctor_cli_missing_path_returns_2(tmp_path, capsys):
    rc = cli.main(["doctor", str(tmp_path / "missing.msg")])

    err = capsys.readouterr().err
    assert rc == 2
    assert "Path not found" in err


def test_check_cli_writes_markdown_and_sets_exit_code(tmp_path):
    kpis = tmp_path / "kpis.json"
    contracts = tmp_path / "contracts.yaml"
    report = tmp_path / "contracts.md"
    kpis.write_text(json.dumps({"U_tip": 0.002}), encoding="utf-8")
    contracts.write_text(
        yaml.safe_dump({
            "contracts": [
                {"name": "down", "type": "direction", "kpi": "U_tip", "direction": "negative"}
            ]
        }),
        encoding="utf-8",
    )

    rc = cli.main(["check", str(kpis), str(contracts), "--out", str(report)])

    assert rc == 1
    assert "Physics Contract Report" in report.read_text(encoding="utf-8")
    assert "FAIL" in report.read_text(encoding="utf-8")


def test_diff_cli_writes_markdown(tmp_path):
    baseline = tmp_path / "baseline.json"
    candidate = tmp_path / "candidate.json"
    report = tmp_path / "diff.md"
    baseline.write_text(json.dumps({"kpis": {"MISES": 100.0}}), encoding="utf-8")
    candidate.write_text(json.dumps({"kpis": {"MISES": 101.0}}), encoding="utf-8")

    rc = cli.main(["diff", str(baseline), str(candidate), "--out", str(report)])

    assert rc == 0
    text = report.read_text(encoding="utf-8")
    assert "Simulation Diff Report" in text
    assert "Change Summary" in text
    assert "MISES" in text
    assert "rtol" in text


def test_diff_cli_accepts_per_kpi_tolerances_json(tmp_path, capsys):
    baseline = tmp_path / "baseline.json"
    candidate = tmp_path / "candidate.json"
    baseline.write_text(json.dumps({"kpis": {"MISES": 100.0, "U_tip": -0.002}}), encoding="utf-8")
    candidate.write_text(json.dumps({"kpis": {"MISES": 125.0, "U_tip": -0.0022}}), encoding="utf-8")

    rc = cli.main([
        "diff",
        str(baseline),
        str(candidate),
        "--tolerances-json",
        '{"MISES": 0.3}',
        "--json",
    ])

    data = json.loads(capsys.readouterr().out)
    assert rc == 1
    assert data["summary"]["sections"]["kpis"]["PASS"] == 1
    assert data["summary"]["sections"]["kpis"]["WARNING"] == 1


def test_memory_search_cli_prints_json(tmp_path, capsys):
    run_dir = tmp_path / "run"
    run_dir.mkdir()
    (run_dir / "capsule.json").write_text(
        json.dumps({
            "schema_version": "0.2.0-dev",
            "run_id": "memory_cli",
            "created_at": "2026-06-02T10:00:00",
            "inputs": {"model_name": "MemoryCliModel"},
            "artifacts": {"Job.log": {"path": "Job.log", "sha256": "abc", "bytes": 10}},
            "provenance": {"status": "COMPLETED"},
            "contracts": {"passed": True, "results": [{"name": "tip_down", "passed": True}]},
        }),
        encoding="utf-8",
    )

    rc = cli.main([
        "memory", "search", str(tmp_path),
        "--query", "MemoryCliModel",
        "--match-mode", "all",
        "--artifact", "Job.log",
        "--contract", "tip",
        "--contracts-passed", "passed",
        "--sort-by", "run_id",
        "--sort-order", "asc",
        "--min-score", "0.5",
        "--json",
    ])

    data = json.loads(capsys.readouterr().out)
    assert rc == 0
    assert data["matches"][0]["run_id"] == "memory_cli"
    assert data["query"]["match_mode"] == "all"
    assert data["query"]["artifact"] == "Job.log"
    assert data["query"]["contract"] == "tip"
    assert data["query"]["contracts_passed"] == "passed"
    assert data["query"]["sort_by"] == "run_id"
    assert data["query"]["sort_order"] == "asc"
    assert data["query"]["min_score"] == 0.5
