# ------------------------------------------------------------------------
# RF-DETR
# Copyright (c) 2025 Roboflow. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
# ------------------------------------------------------------------------
"""Tests for RFDETRCli — PTL Ch4/T4.

Verifies that the CLI module is correctly structured: importable, subclasses LightningCLI, overrides
add_arguments_to_parser, and exposes a callable main() entry point.  CLI integration / smoke tests (--help subprocess,
YAML roundtrip) live in T4-7.
"""

import pytest

# ---------------------------------------------------------------------------
# Structure and importability
# ---------------------------------------------------------------------------


class TestRFDETRCliStructure:
    """RFDETRCli is correctly structured and importable."""

    def test_cli_module_importable(self):
        """rfdetr.training.cli imports without error."""
        import rfdetr.training.cli  # noqa: F401

    def test_rfdetr_cli_importable(self):
        """RFDETRCli can be imported from rfdetr.training.cli."""
        from rfdetr.training.cli import RFDETRCli  # noqa: F401

    def test_main_importable(self):
        """Main() can be imported from rfdetr.training.cli."""
        from rfdetr.training.cli import main  # noqa: F401

    def test_rfdetr_cli_is_lightning_cli_subclass(self):
        """RFDETRCli must subclass pytorch_lightning LightningCLI."""
        from pytorch_lightning.cli import LightningCLI

        from rfdetr.training.cli import RFDETRCli

        assert issubclass(RFDETRCli, LightningCLI)

    def test_main_is_callable(self):
        """Main must be a callable (function, not e.g. a string)."""
        from rfdetr.training.cli import main

        assert callable(main)

    def test_add_arguments_to_parser_is_overridden(self):
        """RFDETRCli overrides add_arguments_to_parser from LightningCLI."""
        from pytorch_lightning.cli import LightningCLI

        from rfdetr.training.cli import RFDETRCli

        assert RFDETRCli.add_arguments_to_parser is not LightningCLI.add_arguments_to_parser

    def test_exported_from_lit_package(self):
        """RFDETRCli is exported from rfdetr.training (appears in __all__)."""
        import rfdetr.training as lit

        assert hasattr(lit, "RFDETRCli")
        assert "RFDETRCli" in lit.__all__


# ---------------------------------------------------------------------------
# Argument linking
# ---------------------------------------------------------------------------


class TestRFDETRCliArgumentLinking:
    """add_arguments_to_parser registers the expected argument links."""

    def _collect_links(self):
        """Instantiate a minimal parser and collect registered link sources."""
        import unittest.mock as mock

        from rfdetr.training.cli import RFDETRCli

        captured = []

        class _FakeParser:
            def link_arguments(self, source, target, **kwargs):
                captured.append({"source": source, "target": target, **kwargs})

            # LightningArgumentParser methods that may be called during setup
            def __getattr__(self, name):
                return mock.MagicMock()

        cli = RFDETRCli.__new__(RFDETRCli)
        cli.add_arguments_to_parser(_FakeParser())
        return captured

    def test_model_config_link_registered(self):
        """model.model_config is linked to data.model_config."""
        links = self._collect_links()
        sources = [lnk["source"] for lnk in links]
        assert "model.model_config" in sources

    def test_train_config_link_registered(self):
        """model.train_config is linked to data.train_config."""
        links = self._collect_links()
        sources = [lnk["source"] for lnk in links]
        assert "model.train_config" in sources

    @pytest.mark.parametrize(
        "source, expected_target",
        [
            pytest.param("model.model_config", "data.model_config", id="model_config"),
            pytest.param("model.train_config", "data.train_config", id="train_config"),
        ],
    )
    def test_link_target(self, source, expected_target):
        """Each link points to the correct data.* target."""
        links = self._collect_links()
        match = next((lnk for lnk in links if lnk["source"] == source), None)
        assert match is not None, f"No link registered for source {source!r}"
        assert match["target"] == expected_target
