"""Tests for profile_manager module."""

import json
import os
from pathlib import Path
from unittest.mock import Mock, patch

import pytest

from hindsight_embed.profile_manager import (
    ProfileInfo,
    ProfileManager,
    ProfilePaths,
    resolve_active_profile,
    validate_profile_exists,
)


@pytest.fixture
def temp_hindsight_dir(tmp_path, monkeypatch):
    """Create a temporary hindsight directory for tests.

    Uses HOME environment variable to make Path.home() return the temp directory.
    This works with the dynamic path resolution in ProfileManager.
    """
    # Clear any CLI profile override for test isolation
    from hindsight_embed.cli import set_cli_profile_override

    set_cli_profile_override(None)

    # Clear HINDSIGHT_EMBED_PROFILE env var
    monkeypatch.delenv("HINDSIGHT_EMBED_PROFILE", raising=False)

    temp_home = tmp_path / "home"
    temp_home.mkdir()
    monkeypatch.setenv("HOME", str(temp_home))

    temp_config = temp_home / ".hindsight"
    temp_config.mkdir()
    return temp_config


@pytest.fixture
def profile_manager(temp_hindsight_dir):
    """Create a ProfileManager with temp directory."""
    return ProfileManager()


class TestProfileManager:
    """Tests for ProfileManager class."""

    def test_create_profile_success(self, profile_manager, temp_hindsight_dir):
        """Test creating a new profile."""
        config = {
            "HINDSIGHT_EMBED_LLM_PROVIDER": "openai",
            "HINDSIGHT_EMBED_LLM_API_KEY": "sk-test",
            "HINDSIGHT_EMBED_LLM_MODEL": "gpt-4o-mini",
        }

        profile_manager.create_profile("test-profile", config)

        # Verify config file was created
        config_path = temp_hindsight_dir / "profiles" / "test-profile.env"
        assert config_path.exists()

        # Verify config contents
        config_content = config_path.read_text()
        assert "HINDSIGHT_EMBED_LLM_PROVIDER=openai" in config_content
        assert "HINDSIGHT_EMBED_LLM_API_KEY=sk-test" in config_content
        assert "HINDSIGHT_EMBED_LLM_MODEL=gpt-4o-mini" in config_content

        # Verify metadata was created
        metadata_path = temp_hindsight_dir / "profiles" / "metadata.json"
        assert metadata_path.exists()

        metadata = json.loads(metadata_path.read_text())
        assert "test-profile" in metadata["profiles"]
        assert "port" in metadata["profiles"]["test-profile"]
        assert "created_at" in metadata["profiles"]["test-profile"]

    def test_create_profile_invalid_name(self, profile_manager):
        """Test creating profile with invalid name fails."""
        config = {"KEY": "value"}

        # Empty name
        with pytest.raises(ValueError, match="Profile name cannot be empty"):
            profile_manager.create_profile("", config)

        # Invalid characters
        with pytest.raises(ValueError, match="Invalid profile name"):
            profile_manager.create_profile("test profile", config)

        with pytest.raises(ValueError, match="Invalid profile name"):
            profile_manager.create_profile("test@profile", config)

    def test_profile_exists(self, profile_manager, temp_hindsight_dir):
        """Test checking if a profile exists."""
        # Default profile doesn't exist initially
        assert not profile_manager.profile_exists("")

        # Create default config
        (temp_hindsight_dir / "embed").write_text("KEY=value")
        assert profile_manager.profile_exists("")

        # Named profile doesn't exist
        assert not profile_manager.profile_exists("test")

        # Create named profile
        profile_manager.create_profile("test", {"KEY": "value"})
        assert profile_manager.profile_exists("test")

    def test_delete_profile(self, profile_manager, temp_hindsight_dir):
        """Test deleting a profile."""
        # Create profile
        profile_manager.create_profile("test-profile", {"KEY": "value"})
        assert profile_manager.profile_exists("test-profile")

        # Delete profile
        profile_manager.delete_profile("test-profile")
        assert not profile_manager.profile_exists("test-profile")

        # Verify all files are removed
        profiles_dir = temp_hindsight_dir / "profiles"
        assert not (profiles_dir / "test-profile.env").exists()
        assert not (profiles_dir / "test-profile.lock").exists()
        assert not (profiles_dir / "test-profile.log").exists()

        # Verify metadata no longer contains profile
        metadata_path = profiles_dir / "metadata.json"
        if metadata_path.exists():
            metadata = json.loads(metadata_path.read_text())
            assert "test-profile" not in metadata.get("profiles", {})

    def test_delete_nonexistent_profile(self, profile_manager):
        """Test deleting a non-existent profile fails."""
        with pytest.raises(ValueError, match="does not exist"):
            profile_manager.delete_profile("nonexistent")

    def test_delete_default_profile_fails(self, profile_manager):
        """Test deleting default profile fails."""
        with pytest.raises(ValueError, match="Cannot delete default profile"):
            profile_manager.delete_profile("")

    def test_set_active_profile(self, profile_manager, temp_hindsight_dir):
        """Test setting active profile."""
        # Create a profile
        profile_manager.create_profile("test-profile", {"KEY": "value"})

        # Set as active
        profile_manager.set_active_profile("test-profile")

        # Verify active profile file was created
        active_file = temp_hindsight_dir / "active_profile"
        assert active_file.exists()
        assert active_file.read_text() == "test-profile"

        # Get active profile
        assert profile_manager.get_active_profile() == "test-profile"

    def test_clear_active_profile(self, profile_manager, temp_hindsight_dir):
        """Test clearing active profile."""
        # Create and set active profile
        profile_manager.create_profile("test-profile", {"KEY": "value"})
        profile_manager.set_active_profile("test-profile")
        assert profile_manager.get_active_profile() == "test-profile"

        # Clear active profile
        profile_manager.set_active_profile(None)
        assert profile_manager.get_active_profile() == ""

        # Verify file was removed
        active_file = temp_hindsight_dir / "active_profile"
        assert not active_file.exists()

    def test_set_active_nonexistent_profile_fails(self, profile_manager):
        """Test setting non-existent profile as active fails."""
        with pytest.raises(ValueError, match="does not exist"):
            profile_manager.set_active_profile("nonexistent")

    def test_list_profiles(self, profile_manager, temp_hindsight_dir):
        """Test listing profiles."""
        # Initially no profiles
        profiles = profile_manager.list_profiles()
        assert len(profiles) == 0

        # Create default config
        (temp_hindsight_dir / "embed").write_text("KEY=value")

        # List profiles - should show default
        profiles = profile_manager.list_profiles()
        assert len(profiles) == 1
        assert profiles[0].name == ""
        assert profiles[0].port == 8888

        # Create named profiles
        profile_manager.create_profile("profile1", {"KEY": "value"})
        profile_manager.create_profile("profile2", {"KEY": "value"})

        # List all profiles
        profiles = profile_manager.list_profiles()
        assert len(profiles) == 3

        # Verify sorting (default first, then alphabetical)
        assert profiles[0].name == ""
        assert profiles[1].name == "profile1"
        assert profiles[2].name == "profile2"

    def test_list_profiles_with_active(self, profile_manager, temp_hindsight_dir):
        """Test listing profiles shows active status."""
        profile_manager.create_profile("profile1", {"KEY": "value"})
        profile_manager.create_profile("profile2", {"KEY": "value"})

        # Set profile1 as active
        profile_manager.set_active_profile("profile1")

        # List profiles
        profiles = profile_manager.list_profiles()

        # Find profile1
        profile1 = next(p for p in profiles if p.name == "profile1")
        profile2 = next(p for p in profiles if p.name == "profile2")

        assert profile1.is_active is True
        assert profile2.is_active is False

    def test_resolve_profile_paths_default(self, profile_manager, temp_hindsight_dir):
        """Test resolving paths for default profile."""
        paths = profile_manager.resolve_profile_paths("")

        assert paths.config == temp_hindsight_dir / "embed"
        assert paths.lock == temp_hindsight_dir / "daemon.lock"
        assert paths.log == temp_hindsight_dir / "daemon.log"
        assert paths.port == 8888

    def test_resolve_profile_paths_named(self, profile_manager, temp_hindsight_dir):
        """Test resolving paths for named profile."""
        # Create profile first
        profile_manager.create_profile("test-profile", {"KEY": "value"})

        paths = profile_manager.resolve_profile_paths("test-profile")

        assert paths.config == temp_hindsight_dir / "profiles" / "test-profile.env"
        assert paths.lock == temp_hindsight_dir / "profiles" / "test-profile.lock"
        assert paths.log == temp_hindsight_dir / "profiles" / "test-profile.log"
        assert 8889 <= paths.port <= 9888  # Port in valid range

    def test_port_allocation_deterministic(self, profile_manager):
        """Test that port allocation is deterministic for same profile name."""
        profile_manager.create_profile("test1", {"KEY": "value"})
        paths1 = profile_manager.resolve_profile_paths("test1")

        # Delete and recreate
        profile_manager.delete_profile("test1")
        profile_manager.create_profile("test1", {"KEY": "value"})
        paths2 = profile_manager.resolve_profile_paths("test1")

        # Port should be the same
        assert paths1.port == paths2.port

    def test_port_allocation_unique(self, profile_manager):
        """Test that different profiles get different ports."""
        profile_manager.create_profile("profile1", {"KEY": "value"})
        profile_manager.create_profile("profile2", {"KEY": "value"})

        paths1 = profile_manager.resolve_profile_paths("profile1")
        paths2 = profile_manager.resolve_profile_paths("profile2")

        # Ports should be different
        assert paths1.port != paths2.port

    def test_daemon_running_status(self, profile_manager):
        """Test that daemon running status is checked correctly."""
        with patch("httpx.Client") as mock_client:
            # Mock successful health check
            mock_response = Mock()
            mock_response.status_code = 200
            mock_client.return_value.__enter__.return_value.get.return_value = mock_response

            profile_manager.create_profile("test", {"KEY": "value"})
            profiles = profile_manager.list_profiles()

            test_profile = next(p for p in profiles if p.name == "test")
            assert test_profile.daemon_running is True

    def test_update_existing_profile(self, profile_manager, temp_hindsight_dir):
        """Test updating an existing profile."""
        # Create profile
        profile_manager.create_profile("test", {"KEY": "old_value"})

        # Update profile
        profile_manager.create_profile("test", {"KEY": "new_value"})

        # Verify config was updated
        config_path = temp_hindsight_dir / "profiles" / "test.env"
        config_content = config_path.read_text()
        assert "KEY=new_value" in config_content
        assert "KEY=old_value" not in config_content

    def test_metadata_persistence(self, profile_manager, temp_hindsight_dir):
        """Test that metadata persists across ProfileManager instances."""
        # Create profile with first manager
        profile_manager.create_profile("test", {"KEY": "value"})

        # Create new manager and verify it sees the profile
        new_manager = ProfileManager()
        assert new_manager.profile_exists("test")

        profiles = new_manager.list_profiles()
        test_profile = next(p for p in profiles if p.name == "test")
        assert test_profile.port > 0

    def test_delete_active_profile_clears_active_file(self, profile_manager, temp_hindsight_dir):
        """Test that deleting active profile clears the active_profile file."""
        profile_manager.create_profile("test", {"KEY": "value"})
        profile_manager.set_active_profile("test")

        # Verify active
        assert profile_manager.get_active_profile() == "test"

        # Delete profile
        profile_manager.delete_profile("test")

        # Active profile should be cleared
        assert profile_manager.get_active_profile() == ""


class TestResolveActiveProfile:
    """Tests for resolve_active_profile function."""

    def test_priority_env_var(self, monkeypatch, profile_manager):
        """Test that HINDSIGHT_EMBED_PROFILE env var has highest priority."""
        # Set up all possible sources
        monkeypatch.setenv("HINDSIGHT_EMBED_PROFILE", "from-env")

        profile_manager.create_profile("from-file", {"KEY": "value"})
        profile_manager.set_active_profile("from-file")

        # Import cli to set override
        from hindsight_embed import cli

        cli.set_cli_profile_override("from-flag")

        # Env var should win
        assert resolve_active_profile() == "from-env"

    def test_priority_cli_flag(self, monkeypatch, profile_manager):
        """Test that CLI flag has second highest priority."""
        # No env var
        monkeypatch.delenv("HINDSIGHT_EMBED_PROFILE", raising=False)

        profile_manager.create_profile("from-file", {"KEY": "value"})
        profile_manager.set_active_profile("from-file")

        # Import cli to set override
        from hindsight_embed import cli

        cli.set_cli_profile_override("from-flag")

        # CLI flag should win
        assert resolve_active_profile() == "from-flag"

    def test_priority_active_file(self, monkeypatch, profile_manager):
        """Test that active file has third priority."""
        # No env var or CLI flag
        monkeypatch.delenv("HINDSIGHT_EMBED_PROFILE", raising=False)

        from hindsight_embed import cli

        cli.set_cli_profile_override(None)

        profile_manager.create_profile("from-file", {"KEY": "value"})
        profile_manager.set_active_profile("from-file")

        # Active file should be used
        assert resolve_active_profile() == "from-file"

    def test_priority_default(self, monkeypatch, profile_manager):
        """Test that default is used when no sources are set."""
        # No env var, CLI flag, or active file
        monkeypatch.delenv("HINDSIGHT_EMBED_PROFILE", raising=False)

        from hindsight_embed import cli

        cli.set_cli_profile_override(None)

        # Default should be used
        assert resolve_active_profile() == ""


class TestValidateProfileExists:
    """Tests for validate_profile_exists function."""

    def test_validate_default_profile_always_passes(self):
        """Test that default profile always passes validation."""
        # Default profile (empty string) should never fail
        validate_profile_exists("")  # Should not raise

    def test_validate_existing_profile_passes(self, profile_manager):
        """Test that existing profile passes validation."""
        profile_manager.create_profile("test", {"KEY": "value"})
        validate_profile_exists("test")  # Should not raise

    def test_validate_nonexistent_profile_fails(self, profile_manager, capsys):
        """Test that non-existent profile fails validation."""
        with pytest.raises(SystemExit) as exc_info:
            validate_profile_exists("nonexistent")

        assert exc_info.value.code == 1

        # Check error message
        captured = capsys.readouterr()
        assert "Profile 'nonexistent' not found" in captured.err
        assert "hindsight-embed configure --profile nonexistent" in captured.err
