"""Tests for the telemetry module."""

import importlib
import importlib.util
from pathlib import Path
from unittest.mock import patch, MagicMock

import pytest

if importlib.util.find_spec("gjalla_precommit") is None:  # pragma: no cover
    pytest.skip("gjalla_precommit package not available", allow_module_level=True)

telemetry_module = importlib.import_module("gjalla_precommit.telemetry")
cli_module = importlib.import_module("gjalla_precommit.cli")


class TestIsEnabled:
    """Tests for _is_enabled()."""

    def test_enabled_via_config(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: true\n")
        assert telemetry_module._is_enabled() is True

    def test_disabled_via_config(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: false\n")
        assert telemetry_module._is_enabled() is False

    def test_disabled_when_not_set(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("api_key: test\n")
        assert telemetry_module._is_enabled() is False

    def test_env_var_overrides_config_to_disable(self, home_dir, monkeypatch):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: true\n")
        monkeypatch.setenv("GJALLA_TELEMETRY", "0")
        assert telemetry_module._is_enabled() is False

    def test_env_var_overrides_config_to_enable(self, home_dir, monkeypatch):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: false\n")
        monkeypatch.setenv("GJALLA_TELEMETRY", "1")
        assert telemetry_module._is_enabled() is True

    def test_env_var_false_values(self, home_dir, monkeypatch):
        for val in ("0", "false", "no", "off"):
            monkeypatch.setenv("GJALLA_TELEMETRY", val)
            assert telemetry_module._is_enabled() is False


class TestGetInstallId:
    """Tests for _get_install_id()."""

    def test_creates_and_persists_uuid(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("api_key: test\n")

        install_id = telemetry_module._get_install_id()
        assert len(install_id) == 36  # UUID format
        assert "-" in install_id

        # Second call returns same ID
        assert telemetry_module._get_install_id() == install_id

    def test_reads_existing_id(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("install_id: my-fixed-id\n")

        assert telemetry_module._get_install_id() == "my-fixed-id"


class TestTrack:
    """Tests for track()."""

    def test_noop_when_disabled(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: false\n")

        with patch("gjalla_precommit.telemetry.httpx") as mock_httpx:
            telemetry_module.track("init")
            mock_httpx.post.assert_not_called()

    def test_sends_when_enabled(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: true\ninstall_id: test-id\n")

        with patch("gjalla_precommit.telemetry.httpx") as mock_httpx:
            telemetry_module.track("status")
            # Give daemon thread a moment to start
            import time
            time.sleep(0.1)
            mock_httpx.post.assert_called_once()
            call_kwargs = mock_httpx.post.call_args
            payload = call_kwargs.kwargs.get("json") or call_kwargs[1].get("json")
            assert payload["command"] == "status"
            assert payload["install_id"] == "test-id"

    def test_no_raise_on_network_failure(self, home_dir):
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: true\ninstall_id: test-id\n")

        with patch("gjalla_precommit.telemetry.httpx") as mock_httpx:
            mock_httpx.post.side_effect = Exception("network error")
            # Should not raise
            telemetry_module.track("sync")
            import time
            time.sleep(0.1)


class TestCheckConsent:
    """Tests for check_consent()."""

    def test_skips_when_env_var_set(self, home_dir, monkeypatch):
        monkeypatch.setenv("GJALLA_TELEMETRY", "0")
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("api_key: test\n")

        with patch("gjalla_precommit.telemetry.Confirm") as mock_confirm:
            telemetry_module.check_consent()
            mock_confirm.ask.assert_not_called()

    def test_skips_when_already_decided(self, home_dir, monkeypatch):
        monkeypatch.delenv("GJALLA_TELEMETRY", raising=False)
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("telemetry: false\n")

        with patch("gjalla_precommit.telemetry.Confirm") as mock_confirm:
            telemetry_module.check_consent()
            mock_confirm.ask.assert_not_called()

    def test_skips_when_not_a_tty(self, home_dir, monkeypatch):
        monkeypatch.delenv("GJALLA_TELEMETRY", raising=False)
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("api_key: test\n")

        with patch("gjalla_precommit.telemetry.sys") as mock_sys, \
             patch("gjalla_precommit.telemetry.Confirm") as mock_confirm:
            mock_sys.stdin.isatty.return_value = False
            # Need to keep os.environ accessible
            mock_sys.modules = {}
            telemetry_module.check_consent()
            mock_confirm.ask.assert_not_called()

        # Should NOT have persisted a decision
        import yaml
        cfg = yaml.safe_load((config_dir / "config.yaml").read_text())
        assert "telemetry" not in cfg

    def test_prompts_and_stores_decision(self, home_dir, monkeypatch):
        monkeypatch.delenv("GJALLA_TELEMETRY", raising=False)
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("api_key: test\n")

        with patch("gjalla_precommit.telemetry.sys") as mock_sys, \
             patch("gjalla_precommit.telemetry.Confirm") as mock_confirm:
            mock_sys.stdin.isatty.return_value = True
            mock_confirm.ask.return_value = True
            telemetry_module.check_consent()
            mock_confirm.ask.assert_called_once()

        # Verify stored
        import yaml
        cfg = yaml.safe_load((config_dir / "config.yaml").read_text())
        assert cfg["telemetry"] is True

    def test_handles_eof_gracefully(self, home_dir, monkeypatch):
        monkeypatch.delenv("GJALLA_TELEMETRY", raising=False)
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text("api_key: test\n")

        with patch("gjalla_precommit.telemetry.sys") as mock_sys, \
             patch("gjalla_precommit.telemetry.Confirm") as mock_confirm:
            mock_sys.stdin.isatty.return_value = True
            mock_confirm.ask.side_effect = EOFError
            telemetry_module.check_consent()

        import yaml
        cfg = yaml.safe_load((config_dir / "config.yaml").read_text())
        assert cfg["telemetry"] is False


class TestTrackIntegration:
    """Integration test for command name tracking via CLI."""

    def test_tracks_correct_command_name(self, home_dir, monkeypatch):
        """result_callback should capture the invoked subcommand name."""
        monkeypatch.delenv("GJALLA_TELEMETRY", raising=False)
        config_dir = home_dir / ".gjalla"
        config_dir.mkdir(parents=True, exist_ok=True)
        (config_dir / "config.yaml").write_text(
            "telemetry: true\ninstall_id: test-id\n"
        )

        from click.testing import CliRunner

        with patch("gjalla_precommit.telemetry.httpx") as mock_httpx:
            runner = CliRunner()
            result = runner.invoke(cli_module.main, ["status"])
            assert result.exit_code == 0

            import time
            time.sleep(0.1)

            mock_httpx.post.assert_called_once()
            payload = (
                mock_httpx.post.call_args.kwargs.get("json")
                or mock_httpx.post.call_args[1].get("json")
            )
            assert payload["command"] == "status"
