"""Tier-specific tests: Lite tier feature set + gating.

Lite unlocks exactly three tools — Find Duplicates, Clean Text,
Standardize Formats — and locks the other six. We test:

- The features map for Lite returns the right three flags (and only
  those three).
- A Lite-tier license passes ``require_feature`` for any of the three
  unlocked flags and raises ``UnsupportedFeatureError`` for any
  locked flag.
- The CLI guard exits with code 2 when a Lite user runs a non-Lite
  tool CLI.

GUI behaviour is covered in ``tests/gui/test_lite_tier.py`` so the
GUI/non-GUI split stays clean.
"""

from __future__ import annotations

import pytest

from src.license import (
    FeatureFlag,
    LicenseManager,
    Tier,
    UnsupportedFeatureError,
)
from src.license.features import FEATURES_BY_TIER, all_features_for_tier


# ---------------------------------------------------------------------------
# Features map
# ---------------------------------------------------------------------------

class TestLiteFeatureMap:
    def test_lite_unlocks_exactly_three_tools(self):
        flags = FEATURES_BY_TIER[Tier.LITE]
        assert flags == frozenset({
            FeatureFlag.DEDUPLICATOR,
            FeatureFlag.TEXT_CLEANER,
            FeatureFlag.FORMAT_STANDARDIZER,
        })

    def test_lite_locks_other_six_tools(self):
        flags = FEATURES_BY_TIER[Tier.LITE]
        locked = {
            FeatureFlag.MISSING_HANDLER,
            FeatureFlag.COLUMN_MAPPER,
            FeatureFlag.OUTLIER_DETECTOR,
            FeatureFlag.MULTI_FILE_MERGER,
            FeatureFlag.VALIDATOR_REPORTER,
            FeatureFlag.PIPELINE_RUNNER,
        }
        for f in locked:
            assert f not in flags

    def test_all_features_for_tier_lite_returns_sorted_tuple(self):
        tup = all_features_for_tier(Tier.LITE)
        assert tup == tuple(sorted(tup))
        assert len(tup) == 3

    def test_core_still_unlocks_every_tool(self):
        """Sanity: adding Lite didn't accidentally narrow Core."""
        assert FEATURES_BY_TIER[Tier.CORE] == frozenset(FeatureFlag)


# ---------------------------------------------------------------------------
# Manager gating
# ---------------------------------------------------------------------------

class TestLiteLicenseGating:
    @pytest.fixture
    def lite_manager(self, isolated_license_path):
        """Pre-activate a Lite license and return the manager."""
        mgr = LicenseManager()
        mgr._mint(name="Lite User", email="lite@example.com", tier=Tier.LITE)
        return mgr

    @pytest.mark.parametrize("feature", [
        FeatureFlag.DEDUPLICATOR,
        FeatureFlag.TEXT_CLEANER,
        FeatureFlag.FORMAT_STANDARDIZER,
    ])
    def test_lite_passes_for_included_features(self, lite_manager, feature):
        lite_manager.require_feature(feature)  # no raise

    @pytest.mark.parametrize("feature", [
        FeatureFlag.MISSING_HANDLER,
        FeatureFlag.COLUMN_MAPPER,
        FeatureFlag.OUTLIER_DETECTOR,
        FeatureFlag.MULTI_FILE_MERGER,
        FeatureFlag.VALIDATOR_REPORTER,
        FeatureFlag.PIPELINE_RUNNER,
    ])
    def test_lite_raises_for_locked_features(self, lite_manager, feature):
        with pytest.raises(UnsupportedFeatureError):
            lite_manager.require_feature(feature)

    def test_lite_state_carries_lite_tier(self, lite_manager):
        state = lite_manager.current_state()
        assert state.tier == "lite"
        assert state.valid is True


# ---------------------------------------------------------------------------
# CLI guard
# ---------------------------------------------------------------------------

class TestLiteCliGuard:
    """In-process tests of the guard. Subprocess equivalents would be
    slower; the unit-level coverage is sufficient because the guard
    is pure Python and the entrypoint plumbing is already tested in
    ``test_license_cli.py``."""

    def _activate_lite(self, monkeypatch, tmp_path):
        monkeypatch.delenv("DATATOOLS_DEV_MODE", raising=False)
        monkeypatch.setenv(
            "DATATOOLS_LICENSE_PATH", str(tmp_path / "license.json"),
        )
        from src.license.manager import reset_singleton_for_tests
        reset_singleton_for_tests()
        from src.license import LicenseManager
        LicenseManager()._mint(
            name="A", email="a@b.com", tier=Tier.LITE,
        )

    def test_guard_passes_for_unlocked_feature(self, monkeypatch, tmp_path):
        self._activate_lite(monkeypatch, tmp_path)
        monkeypatch.setattr("sys.argv", ["prog", "input.csv"])
        from src.cli_license_guard import guard
        # Lite unlocks text cleaner — guard must not raise.
        guard(feature="02_text_cleaner")

    def test_guard_rejects_locked_feature(self, monkeypatch, tmp_path, capsys):
        self._activate_lite(monkeypatch, tmp_path)
        monkeypatch.setattr("sys.argv", ["prog", "input.csv"])
        from src.cli_license_guard import guard
        with pytest.raises(SystemExit) as ei:
            guard(feature="04_missing_handler")
        assert ei.value.code == 2
        captured = capsys.readouterr()
        assert "04_missing_handler" in captured.err
        assert "upgrade" in captured.err.lower() or "tier" in captured.err.lower()

    def test_guard_help_bypasses_feature_check(self, monkeypatch, tmp_path):
        self._activate_lite(monkeypatch, tmp_path)
        monkeypatch.setattr("sys.argv", ["prog", "--help"])
        from src.cli_license_guard import guard
        # --help still bypasses the guard entirely.
        guard(feature="04_missing_handler")
