"""Tests for ONNX and TensorRT-LLM export — config, validation, CLI."""

from unittest.mock import MagicMock
from unittest.mock import patch as mock_patch

from soup_cli.commands.export import SUPPORTED_FORMATS

# ─── Format Support Tests ────────────────────────────────────────────────


class TestExportFormats:
    """Test that new export formats are registered."""

    def test_gguf_format_supported(self):
        assert "gguf" in SUPPORTED_FORMATS

    def test_onnx_format_supported(self):
        assert "onnx" in SUPPORTED_FORMATS

    def test_tensorrt_format_supported(self):
        assert "tensorrt" in SUPPORTED_FORMATS

    def test_format_count(self):
        """v0.53.1 — 7 prior formats + torchao + gguf-ud = 9."""
        assert len(SUPPORTED_FORMATS) == 9
        assert "bitnet" in SUPPORTED_FORMATS
        assert "tq1_0" in SUPPORTED_FORMATS
        assert "torchao" in SUPPORTED_FORMATS
        assert "gguf-ud" in SUPPORTED_FORMATS


# ─── ONNX Export CLI Tests ──────────────────────────────────────────────


class TestOnnxExportCLI:
    """Test ONNX export via CLI."""

    def test_onnx_export_missing_model_path(self, tmp_path):
        """soup export --format onnx should fail if model path doesn't exist."""
        from typer.testing import CliRunner

        from soup_cli.cli import app

        runner = CliRunner()
        result = runner.invoke(
            app, ["export", "--model", str(tmp_path / "nonexistent"), "--format", "onnx"]
        )
        assert result.exit_code != 0

    def test_onnx_export_format_in_help(self):
        """Export help should mention onnx format."""
        from typer.testing import CliRunner

        from soup_cli.cli import app

        runner = CliRunner()
        result = runner.invoke(app, ["export", "--help"])
        assert "onnx" in result.output.lower()

    def test_tensorrt_export_format_in_help(self):
        """Export help should mention tensorrt format."""
        from typer.testing import CliRunner

        from soup_cli.cli import app

        runner = CliRunner()
        result = runner.invoke(app, ["export", "--help"])
        assert "tensorrt" in result.output.lower()


# ─── ONNX Export Function Tests ─────────────────────────────────────────


class TestOnnxExportFunction:
    """Test _export_onnx logic."""

    def test_export_onnx_calls_main_export(self, tmp_path):
        """_export_onnx should call optimum's main_export."""
        model_dir = tmp_path / "model"
        model_dir.mkdir()

        mock_main_export = MagicMock()
        with mock_patch(
            "soup_cli.commands.export.main_export",
            mock_main_export,
            create=True,
        ):
            # Need to patch the import inside the function
            import soup_cli.commands.export as export_mod
            original = getattr(export_mod, "main_export", None)
            try:
                # Inject mock at module level for the lazy import
                with mock_patch.object(
                    export_mod, "_export_onnx",
                    wraps=export_mod._export_onnx,
                ):
                    # Patch the actual import
                    with mock_patch.dict("sys.modules", {
                        "optimum": MagicMock(),
                        "optimum.exporters": MagicMock(),
                        "optimum.exporters.onnx": MagicMock(
                            main_export=mock_main_export
                        ),
                    }):
                        export_mod._export_onnx(model_dir, str(tmp_path / "out"), None)
                        mock_main_export.assert_called_once()
            finally:
                if original is not None:
                    export_mod.main_export = original

    def test_export_onnx_default_output_path(self, tmp_path):
        """Default output path should be model_name + _onnx suffix."""
        model_dir = tmp_path / "my_model"
        model_dir.mkdir()

        mock_main_export = MagicMock()
        with mock_patch.dict("sys.modules", {
            "optimum": MagicMock(),
            "optimum.exporters": MagicMock(),
            "optimum.exporters.onnx": MagicMock(main_export=mock_main_export),
        }):
            import soup_cli.commands.export as export_mod

            export_mod._export_onnx(model_dir, None, None)
            call_kwargs = mock_main_export.call_args[1]
            assert "my_model_onnx" in call_kwargs["output"]


# ─── TensorRT Export Function Tests ──────────────────────────────────────


class TestTensorrtExportFunction:
    """Test _export_tensorrt logic."""

    def test_tensorrt_export_calls_subprocess(self, tmp_path):
        """TensorRT export should call subprocess for checkpoint conversion."""
        model_dir = tmp_path / "model"
        model_dir.mkdir()

        mock_result = MagicMock()
        mock_result.returncode = 0

        with mock_patch.dict("sys.modules", {
            "optimum": MagicMock(),
            "optimum.exporters": MagicMock(),
            "optimum.exporters.onnx": MagicMock(),
            "tensorrt_llm": MagicMock(),
        }), mock_patch("subprocess.run", return_value=mock_result) as mock_run:
            import soup_cli.commands.export as export_mod

            export_mod._export_tensorrt(
                model_dir, str(tmp_path / "trt_out"), None
            )
            # Should call subprocess at least twice (checkpoint + build)
            assert mock_run.call_count >= 2


# ─── Unsupported Format Test ─────────────────────────────────────────────


class TestExportUnsupportedFormat:
    """Test that unsupported formats are rejected."""

    def test_invalid_format_rejected(self, tmp_path):
        """Export with unsupported format should exit with error."""
        from typer.testing import CliRunner

        from soup_cli.cli import app

        model_dir = tmp_path / "model"
        model_dir.mkdir()

        runner = CliRunner()
        result = runner.invoke(
            app,
            ["export", "--model", str(model_dir), "--format", "safetensors"],
        )
        assert result.exit_code != 0
        assert "Unsupported format" in result.output
