"""GUI activation + license-gate tests.

These exercise the chrome-level gate that ``hide_streamlit_chrome``
installs: when no valid license is on disk, every page renders the
activation form instead of the page body, and tool widgets do NOT
appear. We test against the Find Duplicates page since it's the smallest
real-world tool that depends on chrome.

The autouse fixture in ``tests/conftest.py`` sets
``DATATOOLS_DEV_MODE=1``, which the GUI gate respects. Each test
below uses ``monkeypatch`` to clear that env var so the real gate
fires; ``isolated_license_path`` then redirects the manager to a
tmp file.
"""

from __future__ import annotations

import pytest

from streamlit.testing.v1 import AppTest

from .conftest import collected_text, stash_upload, with_language


@pytest.fixture
def no_license_env(monkeypatch, tmp_path):
    """Clear dev mode and point the license at a fresh empty 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()
    yield tmp_path / "license.json"
    reset_singleton_for_tests()


@pytest.fixture
def trial_license(no_license_env):
    """Pre-activate a 1-year trial license for tests that need to
    pass the gate."""
    from src.license import LicenseManager, Tier
    mgr = LicenseManager()
    mgr.issue_trial(name="Test User", email="test@example.com")
    yield mgr


class TestGateBlocksWithoutLicense:
    """When no license file exists, every page should render the
    activation form and short-circuit the tool body."""

    def test_home_renders_activation_form(self, no_license_env, home_app):
        home_app.run()
        text = collected_text(home_app)
        assert "Activate DataTools" in text or "Activar DataTools" in text

    def test_dedup_page_does_not_render_tool_widgets(
        self, no_license_env, app_factory,
    ):
        app = app_factory("1_Deduplicator")
        app.run()
        # Without a license, the page should NOT have the dedup-
        # specific advanced-options expander or Find Duplicates button.
        labels = [b.label for b in app.button]
        assert not any("Find Duplicates" in lbl for lbl in labels), (
            f"tool widgets leaked past the gate; got: {labels}"
        )

    def test_activation_form_localizes_to_spanish(
        self, no_license_env, home_app,
    ):
        with_language(home_app, "es")
        home_app.run()
        text = collected_text(home_app)
        assert "Activar DataTools" in text

    def test_sidebar_shows_not_activated(self, no_license_env, home_app):
        home_app.run()
        # Sidebar caption "🔒 Not activated".
        captions = [c.value for c in home_app.sidebar.caption]
        joined = " ".join(captions)
        assert "Not activated" in joined or "Sin activar" in joined


class TestGatePassesWithTrialLicense:
    def test_home_renders_full_grid(self, trial_license, home_app):
        home_app.run()
        text = collected_text(home_app)
        # With a valid license, the activation form should NOT be the
        # primary content; we should see the home tagline + tool cards.
        assert "Clean. Normalize. Transform." in text
        assert "Activate DataTools" not in text  # form not shown inline

    def test_sidebar_shows_active_status(self, trial_license, home_app):
        home_app.run()
        captions = " ".join(c.value for c in home_app.sidebar.caption)
        # "Trial · 364 days left" (give or take one).
        assert "Trial" in captions or "Prueba" in captions
        assert "days left" in captions or "días" in captions

    def test_dedup_page_renders_tool_widgets(
        self, trial_license, app_factory, small_csv_bytes,
    ):
        app = app_factory("1_Deduplicator")
        stash_upload(app, name="messy.csv", data=small_csv_bytes)
        app.run()
        labels = [b.label for b in app.button]
        assert any("Find Duplicates" in lbl for lbl in labels), (
            f"tool widgets blocked by gate even with valid license; "
            f"got: {labels}"
        )


class TestActivationFormSubmission:
    """End-to-end: paste a generated blob into the inline form and
    confirm the gate releases on next render."""

    def test_paste_blob_activates_and_unlocks(
        self, no_license_env, home_app,
    ):
        # Generate a blob the same way scripts/generate_license.py does.
        from src.license import LicenseManager, Tier
        from src.license.crypto import encode_blob
        mint_mgr = LicenseManager()
        # Use a separate tmp path so the trial we mint doesn't fight
        # with the manager the GUI will use.
        from tempfile import mkstemp
        from pathlib import Path
        _, p = mkstemp(suffix=".json")
        mint_mgr._path = Path(p)
        lic = mint_mgr._mint(
            name="Buyer", email="buyer@example.com", tier=Tier.CORE,
        )
        blob = encode_blob(lic.to_dict())
        # Wipe the temporary mint manager so its file doesn't collide.
        mint_mgr.deactivate()

        # Now drive the real GUI manager.
        home_app.run()
        # The activation form is inline in chrome — its widget keys
        # are prefixed ``gate_``.
        home_app.text_input(key="gate_name").set_value("Buyer").run()
        home_app.text_input(key="gate_email").set_value("buyer@example.com").run()
        home_app.text_area(key="gate_blob").set_value(blob).run()
        # Submit primary form button.
        submit = next(
            b for b in home_app.button
            if b.label in ("Activate", "Apply renewal")
        )
        submit.click().run()

        # After activation the page reruns and the activation form
        # should be gone — we should see the home page proper.
        text = collected_text(home_app)
        assert "Clean. Normalize. Transform." in text

    def test_trial_button_absent_paid_only(self, no_license_env, home_app):
        """v1.6 dropped the user-facing trial flow — paid licenses only.
        Verify the trial button is not on the activation form."""
        home_app.run()
        labels = [b.label for b in home_app.button]
        for lbl in labels:
            assert "trial" not in lbl.lower(), (
                f"trial button leaked into activation form: {lbl!r}"
            )
            assert "prueba" not in lbl.lower(), (
                f"Spanish trial button leaked into form: {lbl!r}"
            )


class TestActivationPageDirect:
    """``pages/_Activate.py`` renders the same form regardless of
    license state — buyer can revisit it to review or deactivate."""

    def test_activate_page_renders_with_valid_license(
        self, trial_license, app_factory,
    ):
        app = app_factory("_Activate")
        app.run()
        text = collected_text(app)
        # Page title localized.
        assert "Activate DataTools" in text
        # Deactivate option only shown after activation.
        labels = [b.label for b in app.button]
        assert any("Deactivate" in lbl for lbl in labels)

    def test_activate_page_renders_without_license(
        self, no_license_env, app_factory,
    ):
        app = app_factory("_Activate")
        app.run()
        text = collected_text(app)
        assert "Activate DataTools" in text
        # Deactivate button should NOT appear when nothing is active.
        labels = [b.label for b in app.button]
        assert not any("Deactivate" in lbl for lbl in labels)


class TestSidebarRenewalWarning:
    """A license with <30 days remaining surfaces a sidebar warning."""

    def test_renewal_warning_appears_under_30_days(
        self, no_license_env, home_app, monkeypatch,
    ):
        # Mint a license with 7 days left.
        from datetime import datetime, timedelta, timezone
        from src.license import License, LicenseManager, Tier
        from src.license import crypto as _crypto
        from src.license.features import all_features_for_tier

        future = (datetime.now(timezone.utc) + timedelta(days=7)).strftime(
            "%Y-%m-%dT%H:%M:%SZ"
        )
        lic = License(
            name="X", email="x@x.com",
            license_key="DT1-CORE-EXPIRING",
            tier=Tier.CORE,
            features=all_features_for_tier(Tier.CORE),
            issued_at="2026-05-13T00:00:00Z",
            expires_at=future,
            signature="",
        )
        sig = _crypto.sign(lic.to_canonical_dict())
        signed = License(**{**lic.__dict__, "signature": sig})
        LicenseManager().save(signed)

        home_app.run()
        sidebar_warnings = [w.body for w in home_app.sidebar.warning if w.body]
        joined = " ".join(sidebar_warnings)
        assert "expires in" in joined.lower() or "caduca en" in joined.lower(), (
            f"expected renewal warning in sidebar; got: {sidebar_warnings}"
        )


class TestLicenseStatusBadgeI18n:
    """Sidebar status badge tier name must localize."""

    def test_core_tier_localizes_in_spanish(
        self, no_license_env, home_app, monkeypatch,
    ):
        from src.license import LicenseManager, Tier
        LicenseManager()._mint(
            name="X", email="x@x.com", tier=Tier.CORE,
        )
        with_language(home_app, "es")
        home_app.run()
        captions = " ".join(c.value for c in home_app.sidebar.caption)
        # The es pack maps ``license.tier_core`` to "Core" — same word
        # in Spanish — but the surrounding template (``días restantes``)
        # localizes.
        assert "días restantes" in captions, (
            f"Spanish status label missing; sidebar captions: {captions}"
        )
