"""Tests for OAuth authentication module."""

import json

import pytest

from autodebug import auth
from autodebug.oauth import generate_pkce_pair


class TestPKCEGeneration:
    """Test PKCE code generation (now in oauth module)."""

    def test_generate_pkce_pair_returns_pair(self):
        """Test that PKCE pair generation returns verifier and challenge."""
        pair = generate_pkce_pair()
        assert isinstance(pair.verifier, str)
        assert isinstance(pair.challenge, str)

    def test_generate_pkce_pair_verifier_length(self):
        """Test that verifier is appropriate length."""
        pair = generate_pkce_pair()
        # Base64url encoding of 64 bytes = ~86 chars
        assert 43 <= len(pair.verifier) <= 128

    def test_generate_pkce_pair_challenge_is_sha256(self):
        """Test that challenge is SHA256 of verifier."""
        import base64
        import hashlib

        pair = generate_pkce_pair()

        # Verify the challenge is correct SHA256 hash
        expected_digest = hashlib.sha256(pair.verifier.encode()).digest()
        expected_challenge = base64.urlsafe_b64encode(expected_digest).rstrip(b"=").decode()

        assert pair.challenge == expected_challenge

    def test_generate_pkce_pair_unique(self):
        """Test that each call generates unique pairs."""
        pairs = [generate_pkce_pair() for _ in range(10)]
        verifiers = [p.verifier for p in pairs]
        challenges = [p.challenge for p in pairs]

        assert len(set(verifiers)) == 10
        assert len(set(challenges)) == 10


class TestCredentialsStorage:
    """Test credentials file operations."""

    def _patch_paths(self, tmp_path, monkeypatch):
        """Patch both auth and config module paths."""
        creds_file = tmp_path / "credentials.json"
        monkeypatch.setattr(auth, "HIKAFLOW_DIR", tmp_path)
        monkeypatch.setattr(auth, "CREDENTIALS_FILE", creds_file)
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", creds_file)
        monkeypatch.setattr("autodebug.config.CONFIG_DIR", tmp_path)
        return creds_file

    def test_save_credentials_creates_file(self, tmp_path, monkeypatch):
        """Test that save_credentials creates the file."""
        creds_file = self._patch_paths(tmp_path, monkeypatch)

        auth.save_credentials({"token": "test123"})

        assert creds_file.exists()
        data = json.loads(creds_file.read_text())
        assert data["token"] == "test123"

    def test_save_credentials_merges_with_existing(self, tmp_path, monkeypatch):
        """Test that save_credentials merges with existing data."""
        creds_file = self._patch_paths(tmp_path, monkeypatch)
        creds_file.write_text(json.dumps({"existing_key": "value"}))

        auth.save_credentials({"token": "new_token"})

        data = json.loads(creds_file.read_text())
        assert data["existing_key"] == "value"
        assert data["token"] == "new_token"

    def test_save_credentials_sets_permissions(self, tmp_path, monkeypatch):
        """Test that file permissions are set to 600."""
        creds_file = self._patch_paths(tmp_path, monkeypatch)

        auth.save_credentials({"token": "test"})

        mode = creds_file.stat().st_mode
        assert mode & 0o777 == 0o600

    def test_get_credentials_returns_none_when_missing(self, tmp_path, monkeypatch):
        """Test get_credentials returns None when file doesn't exist."""
        self._patch_paths(tmp_path, monkeypatch)

        assert auth.get_credentials() is None

    def test_get_credentials_returns_data(self, tmp_path, monkeypatch):
        """Test get_credentials returns saved data."""
        creds_file = self._patch_paths(tmp_path, monkeypatch)
        creds_file.write_text(json.dumps({"token": "saved_token"}))

        creds = auth.get_credentials()
        assert creds["token"] == "saved_token"

    def test_get_credentials_handles_invalid_json(self, tmp_path, monkeypatch):
        """Test get_credentials handles corrupted file gracefully."""
        creds_file = self._patch_paths(tmp_path, monkeypatch)
        creds_file.write_text("not valid json {{{")

        # config.load_credentials catches JSONDecodeError and returns {}
        # get_credentials() returns None for empty creds
        result = auth.get_credentials()
        assert result is None

    def test_clear_credentials_removes_file(self, tmp_path, monkeypatch):
        """Test clear_credentials removes the file."""
        creds_file = self._patch_paths(tmp_path, monkeypatch)
        creds_file.write_text(json.dumps({"token": "test"}))

        auth.clear_credentials()

        assert not creds_file.exists()

    def test_clear_credentials_no_error_if_missing(self, tmp_path, monkeypatch):
        """Test clear_credentials doesn't error if file doesn't exist."""
        self._patch_paths(tmp_path, monkeypatch)

        # Should not raise
        auth.clear_credentials()


class TestIsAuthenticated:
    """Test authentication status check."""

    def test_is_authenticated_false_when_no_file(self, tmp_path, monkeypatch):
        """Test is_authenticated returns False when no credentials."""
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", tmp_path / "credentials.json")
        assert auth.is_authenticated() is False

    def test_is_authenticated_false_when_no_token(self, tmp_path, monkeypatch):
        """Test is_authenticated returns False when no token key."""
        creds_file = tmp_path / "credentials.json"
        creds_file.write_text(json.dumps({"other_key": "value"}))
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", creds_file)

        assert auth.is_authenticated() is False

    def test_is_authenticated_true_when_token_exists(self, tmp_path, monkeypatch):
        """Test is_authenticated returns True when token exists."""
        creds_file = tmp_path / "credentials.json"
        creds_file.write_text(json.dumps({"token": "valid_token"}))
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", creds_file)

        assert auth.is_authenticated() is True


class TestLoadConfig:
    """Test configuration loading."""

    def test_load_config_default_api_base(self, tmp_path, monkeypatch):
        """Test load_config returns default API base."""
        monkeypatch.setattr(auth, "CONFIG_FILE", tmp_path / "config.json")
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", tmp_path / "credentials.json")
        monkeypatch.delenv("HIKAFLOW_ENDPOINT", raising=False)

        config = auth.load_config()
        assert config["api_base"] == "https://get.hikaflow.com"

    def test_load_config_env_override(self, tmp_path, monkeypatch):
        """Test load_config respects environment variable."""
        monkeypatch.setattr(auth, "CONFIG_FILE", tmp_path / "config.json")
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", tmp_path / "credentials.json")
        monkeypatch.setenv("HIKAFLOW_ENDPOINT", "https://custom.api.com")

        config = auth.load_config()
        assert config["api_base"] == "https://custom.api.com"

    def test_load_config_merges_credentials(self, tmp_path, monkeypatch):
        """Test load_config merges credentials into config."""
        creds_file = tmp_path / "credentials.json"
        creds_file.write_text(json.dumps({"token": "my_token", "user_id": "123"}))
        monkeypatch.setattr(auth, "CONFIG_FILE", tmp_path / "config.json")
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", creds_file)

        config = auth.load_config()
        assert config["token"] == "my_token"
        assert config["user_id"] == "123"


class TestSaveToken:
    """Test save_token convenience function."""

    def test_save_token_saves_to_credentials(self, tmp_path, monkeypatch):
        """Test save_token saves the token correctly."""
        creds_file = tmp_path / "credentials.json"
        monkeypatch.setattr(auth, "HIKAFLOW_DIR", tmp_path)
        monkeypatch.setattr(auth, "CREDENTIALS_FILE", creds_file)
        monkeypatch.setattr("autodebug.config.CREDENTIALS_PATH", creds_file)
        monkeypatch.setattr("autodebug.config.CONFIG_DIR", tmp_path)

        auth.save_token("my-jwt-token")

        data = json.loads(creds_file.read_text())
        assert data["token"] == "my-jwt-token"
