# #region TestSeedSupersetLoadTest [C:2] [TYPE Module] [SEMANTICS test,superset,seed,load-test]
# @BRIEF Tests for pure logic functions in seed_superset_load_test.py (no real Superset API).
# @RELATION BINDS_TO -> [SeedSupersetLoadTestScript]
# @TEST_EDGE: parse_args_defaults -> Returns default values
# @TEST_EDGE: parse_args_custom -> Overrides with CLI args
# @TEST_EDGE: extract_result_payload_wrapped -> Unwraps 'result' key
# @TEST_EDGE: extract_result_payload_plain -> Returns payload as-is
# @TEST_EDGE: extract_result_payload_not_dict -> Returns payload
# @TEST_EDGE: extract_created_id_direct -> Reads id key
# @TEST_EDGE: extract_created_id_nested -> Reads result.id
# @TEST_EDGE: extract_created_id_missing -> Returns None
# @TEST_EDGE: generate_unique_name_unique -> No collisions
# @TEST_EDGE: generate_unique_name_collision -> Skips used names
# @TEST_EDGE: resolve_target_envs_ok -> Returns resolved envs
# @TEST_EDGE: resolve_target_envs_missing -> Raises ValueError
# @TEST_EDGE: seed_superset_load_data_dry_run -> No side effects with --dry-run
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch, PropertyMock

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))


# =============================================================================
# _parse_args
# =============================================================================

class TestParseArgs:
    """Tests for seed_superset_load_test._parse_args()."""

    def test_defaults(self):
        """Happy: no CLI args returns defaults."""
        from src.scripts.seed_superset_load_test import _parse_args
        with patch("sys.argv", ["prog"]):
            args = _parse_args()
        assert args.envs == ["ss1", "ss2"]
        assert args.charts == 10000
        assert args.dashboards == 500
        assert args.template_pool_size == 200
        assert args.seed is None
        assert args.max_errors == 100
        assert args.dry_run is False

    def test_custom_values(self):
        """Edge: all args overridden."""
        from src.scripts.seed_superset_load_test import _parse_args
        argv = [
            "prog",
            "--envs", "prod", "staging",
            "--charts", "50",
            "--dashboards", "10",
            "--template-pool-size", "5",
            "--seed", "42",
            "--max-errors", "5",
            "--dry-run",
        ]
        with patch("sys.argv", argv):
            args = _parse_args()
        assert args.envs == ["prod", "staging"]
        assert args.charts == 50
        assert args.dashboards == 10
        assert args.template_pool_size == 5
        assert args.seed == 42
        assert args.max_errors == 5
        assert args.dry_run is True


# =============================================================================
# _extract_result_payload
# =============================================================================

class TestExtractResultPayload:
    """Tests for seed_superset_load_test._extract_result_payload()."""

    def test_wrapped_in_result(self):
        """Happy: payload has result dict, returns it."""
        from src.scripts.seed_superset_load_test import _extract_result_payload
        payload = {"result": {"id": 1, "name": "test"}}
        result = _extract_result_payload(payload)
        assert result == {"id": 1, "name": "test"}

    def test_plain_payload(self):
        """Edge: no result key returns payload as-is."""
        from src.scripts.seed_superset_load_test import _extract_result_payload
        payload = {"id": 1, "name": "test"}
        result = _extract_result_payload(payload)
        assert result == payload

    def test_result_not_dict(self):
        """Edge: result is a list, returns original payload."""
        from src.scripts.seed_superset_load_test import _extract_result_payload
        payload = {"result": [1, 2, 3]}
        result = _extract_result_payload(payload)
        assert result == payload


# =============================================================================
# _extract_created_id
# =============================================================================

class TestExtractCreatedId:
    """Tests for seed_superset_load_test._extract_created_id()."""

    def test_direct_id(self):
        """Happy: payload has direct id key."""
        from src.scripts.seed_superset_load_test import _extract_created_id
        assert _extract_created_id({"id": 42}) == 42

    def test_nested_in_result(self):
        """Happy: id nested under result key."""
        from src.scripts.seed_superset_load_test import _extract_created_id
        assert _extract_created_id({"result": {"id": 99}}) == 99

    def test_missing_id(self):
        """Edge: no id anywhere returns None."""
        from src.scripts.seed_superset_load_test import _extract_created_id
        assert _extract_created_id({"foo": "bar"}) is None

    def test_id_not_int(self):
        """Edge: id is non-int returns None."""
        from src.scripts.seed_superset_load_test import _extract_created_id
        assert _extract_created_id({"id": "abc"}) is None

    def test_result_without_id(self):
        """Edge: result exists but has no id."""
        from src.scripts.seed_superset_load_test import _extract_created_id
        assert _extract_created_id({"result": {"foo": "bar"}}) is None


# =============================================================================
# _generate_unique_name
# =============================================================================

class TestGenerateUniqueName:
    """Tests for seed_superset_load_test._generate_unique_name()."""

    def test_generates_unique_names(self):
        """Happy: generates multiple unique names without collision."""
        from src.scripts.seed_superset_load_test import _generate_unique_name
        import random
        rng = random.Random(42)
        used: set[str] = set()
        names = [_generate_unique_name("lt_chart", used, rng) for _ in range(50)]
        assert len(names) == 50
        assert len(set(names)) == 50  # all unique
        assert all(n.startswith("lt_chart_") for n in names)

    def test_collision_avoidance(self):
        """Edge: generates name, verifies it's added to used set."""
        from src.scripts.seed_superset_load_test import _generate_unique_name
        import random
        rng = random.Random(42)
        used: set[str] = set()

        # Verify the function adds the name to used and returns a valid name
        name = _generate_unique_name("lt_test", used, rng)
        assert name.startswith("lt_test_")
        assert len(name) > len("lt_test_")
        # The name is added to used by the function
        assert name in used
        # Generate another — it should be different
        name2 = _generate_unique_name("lt_test", used, rng)
        assert name2 != name
        assert name2 in used
        assert len(used) == 2


# =============================================================================
# _resolve_target_envs (mocked config)
# =============================================================================

class TestResolveTargetEnvs:
    """Tests for seed_superset_load_test._resolve_target_envs()."""

    def _make_env(self, env_id: str, name: str = ""):
        """Helper to build a minimal Environment with required fields."""
        from src.core.config_models import Environment
        return Environment(
            id=env_id,
            name=name or env_id,
            url=f"http://{env_id}.test",
            username="test_user",
            password="test_pass",
        )

    @patch("src.scripts.seed_superset_load_test.ConfigManager")
    def test_resolves_configured_envs(self, MockConfigManager):
        """Happy: returns resolved env objects."""
        from src.scripts.seed_superset_load_test import _resolve_target_envs

        env1 = self._make_env("ss1", "Env1")
        env2 = self._make_env("ss2", "Env2")

        mock_mgr = MockConfigManager.return_value
        mock_mgr.get_environments.return_value = [env1, env2]

        result = _resolve_target_envs(["ss1", "ss2"])
        assert "ss1" in result
        assert "ss2" in result
        assert result["ss1"].name == "Env1"

    @patch("src.scripts.seed_superset_load_test.ConfigManager")
    def test_missing_env_raises(self, MockConfigManager):
        """Edge: requested env not in config raises ValueError."""
        from src.scripts.seed_superset_load_test import _resolve_target_envs

        env1 = self._make_env("ss1")

        mock_mgr = MockConfigManager.return_value
        mock_mgr.get_environments.return_value = [env1]

        with pytest.raises(ValueError, match="Environment 'ss2' not found"):
            _resolve_target_envs(["ss1", "ss2"])

    @patch("src.scripts.seed_superset_load_test.ConfigManager")
    def test_no_environments_empty_result(self, MockConfigManager):
        """Edge: no configured envs returns empty dict, no error for empty request."""
        from src.scripts.seed_superset_load_test import _resolve_target_envs

        mock_mgr = MockConfigManager.return_value
        mock_mgr.get_environments.return_value = []

        # Empty request: no error
        result = _resolve_target_envs([])
        assert result == {}

    @patch("src.scripts.seed_superset_load_test.ConfigManager")
    def test_partial_resolution(self, MockConfigManager):
        """Edge: only some envs resolved, raises on missing."""
        from src.scripts.seed_superset_load_test import _resolve_target_envs

        env1 = self._make_env("ss1")

        mock_mgr = MockConfigManager.return_value
        mock_mgr.get_environments.return_value = [env1]

        with pytest.raises(ValueError, match="Environment 'ss2' not found"):
            _resolve_target_envs(["ss1", "ss2"])


# =============================================================================
# seed_superset_load_data (dry_run mode only — no real API calls)
# =============================================================================

class TestSeedSupersetLoadData:
    """Tests for seed_superset_load_data — dry run and early returns."""

    def _make_env(self, env_id: str, name: str = ""):
        """Helper to build a minimal Environment with required fields."""
        from src.core.config_models import Environment
        return Environment(
            id=env_id,
            name=name or env_id,
            url=f"http://{env_id}.test",
            username="test_user",
            password="test_pass",
        )

    @patch("src.scripts.seed_superset_load_test._resolve_target_envs")
    @patch("src.scripts.seed_superset_load_test.SupersetClient")
    def test_dry_run_returns_summary(self, MockClient, mock_resolve):
        """Happy: dry_run=True returns summary without API writes."""
        from src.scripts.seed_superset_load_test import seed_superset_load_data

        env1 = self._make_env("ss1", "Env1")
        mock_resolve.return_value = {"ss1": env1}

        # Mock client
        mock_client = MagicMock()
        mock_client.network.fetch_paginated_data.return_value = [
            {"id": 1, "slice_name": "chart1", "viz_type": "bar"}
        ]
        mock_client.get_chart.return_value = {
            "result": {"datasource_id": 10, "datasource_type": "table", "params": "{}"}
        }
        MockClient.return_value = mock_client

        args = MagicMock()
        args.seed = 42
        args.envs = ["ss1"]
        args.charts = 100
        args.dashboards = 10
        args.template_pool_size = 5
        args.max_errors = 100
        args.dry_run = True

        result = seed_superset_load_data(args)

        assert result["dry_run"] is True
        assert result["charts_target"] == 100
        assert result["dashboards_target"] == 10
        assert result["templates_by_env"] == {"ss1": 1}
        # In dry_run mode, no POST requests are made
        mock_client.network.request.assert_not_called()
# #endregion TestSeedSupersetLoadTest
