"""Tests for token storage."""

import json
import os
import stat
from datetime import datetime, timedelta
from pathlib import Path

import pytest

from platform_2step_mcp.auth.models import TokenData
from platform_2step_mcp.auth.storage import TokenStorage


@pytest.fixture
def temp_storage_path(tmp_path: Path) -> str:
    """Create a temporary storage path."""
    return str(tmp_path / "tokens.json")


@pytest.fixture
def storage(temp_storage_path: str) -> TokenStorage:
    """Create a TokenStorage instance with temp path."""
    return TokenStorage(temp_storage_path)


@pytest.fixture
def sample_token() -> TokenData:
    """Create sample token data."""
    return TokenData(
        access_token="test-access-token",
        refresh_token="test-refresh-token",
        expires_at=datetime(2024, 1, 15, 10, 0, 0),
        scope="read create_pending",
    )


class TestTokenStorage:
    """Tests for TokenStorage class."""

    def test_init_with_default_path(self):
        """Test initialization with default path."""
        storage = TokenStorage()
        assert "~/.platform-mcp/tokens.json" in str(storage.path) or ".platform-mcp/tokens.json" in str(storage.path)

    def test_init_with_custom_path(self, temp_storage_path: str):
        """Test initialization with custom path."""
        storage = TokenStorage(temp_storage_path)
        assert str(storage.path) == temp_storage_path

    def test_init_expands_user_path(self, tmp_path: Path):
        """Test that ~ is expanded to user home."""
        storage = TokenStorage("~/.test-platform-mcp/tokens.json")
        assert "~" not in str(storage.path)

    def test_save_creates_directory(self, tmp_path: Path, sample_token: TokenData):
        """Test that save creates the directory if it doesn't exist."""
        nested_path = tmp_path / "nested" / "dir" / "tokens.json"
        storage = TokenStorage(str(nested_path))

        storage.save(sample_token)

        assert nested_path.exists()

    def test_save_writes_json(self, storage: TokenStorage, sample_token: TokenData):
        """Test that save writes valid JSON."""
        storage.save(sample_token)

        content = storage.path.read_text()
        data = json.loads(content)

        assert data["access_token"] == "test-access-token"
        assert data["refresh_token"] == "test-refresh-token"
        assert data["expires_at"] == "2024-01-15T10:00:00"
        assert data["scope"] == "read create_pending"

    def test_save_sets_file_permissions(self, storage: TokenStorage, sample_token: TokenData):
        """Test that save sets secure file permissions (0o600)."""
        storage.save(sample_token)

        file_stat = os.stat(storage.path)
        permissions = stat.S_IMODE(file_stat.st_mode)

        assert permissions == 0o600

    def test_save_sets_directory_permissions(
        self, tmp_path: Path, sample_token: TokenData
    ):
        """Test that save sets secure directory permissions (0o700)."""
        nested_path = tmp_path / "secure-dir" / "tokens.json"
        storage = TokenStorage(str(nested_path))

        storage.save(sample_token)

        dir_stat = os.stat(nested_path.parent)
        permissions = stat.S_IMODE(dir_stat.st_mode)

        assert permissions == 0o700

    def test_load_returns_none_when_no_file(self, storage: TokenStorage):
        """Test that load returns None when file doesn't exist."""
        result = storage.load()
        assert result is None

    def test_load_returns_token_data(self, storage: TokenStorage, sample_token: TokenData):
        """Test that load returns TokenData from saved tokens."""
        storage.save(sample_token)

        result = storage.load()

        assert result is not None
        assert result.access_token == sample_token.access_token
        assert result.refresh_token == sample_token.refresh_token
        assert result.expires_at == sample_token.expires_at
        assert result.scope == sample_token.scope

    def test_load_handles_invalid_json(self, storage: TokenStorage):
        """Test that load handles invalid JSON gracefully."""
        storage.path.parent.mkdir(parents=True, exist_ok=True)
        storage.path.write_text("not valid json")

        result = storage.load()
        assert result is None

    def test_load_handles_missing_fields(self, storage: TokenStorage):
        """Test that load handles missing required fields."""
        storage.path.parent.mkdir(parents=True, exist_ok=True)
        storage.path.write_text(json.dumps({"incomplete": "data"}))

        result = storage.load()
        assert result is None

    def test_clear_removes_file(self, storage: TokenStorage, sample_token: TokenData):
        """Test that clear removes the token file."""
        storage.save(sample_token)
        assert storage.path.exists()

        storage.clear()

        assert not storage.path.exists()

    def test_clear_handles_no_file(self, storage: TokenStorage):
        """Test that clear doesn't error when file doesn't exist."""
        assert not storage.path.exists()
        storage.clear()  # Should not raise

    def test_exists_returns_false_when_no_file(self, storage: TokenStorage):
        """Test exists returns False when file doesn't exist."""
        assert not storage.exists()

    def test_exists_returns_true_when_file_exists(
        self, storage: TokenStorage, sample_token: TokenData
    ):
        """Test exists returns True when file exists."""
        storage.save(sample_token)
        assert storage.exists()

    def test_roundtrip(self, storage: TokenStorage, sample_token: TokenData):
        """Test complete save/load roundtrip."""
        storage.save(sample_token)
        loaded = storage.load()

        assert loaded is not None
        assert loaded.access_token == sample_token.access_token
        assert loaded.refresh_token == sample_token.refresh_token
        assert loaded.expires_at == sample_token.expires_at
        assert loaded.scope == sample_token.scope

    def test_save_without_refresh_token(self, storage: TokenStorage):
        """Test saving token without refresh token."""
        token = TokenData(
            access_token="test-token",
            refresh_token=None,
            expires_at=datetime.utcnow() + timedelta(hours=1),
            scope="read",
        )

        storage.save(token)
        loaded = storage.load()

        assert loaded is not None
        assert loaded.access_token == "test-token"
        assert loaded.refresh_token is None
