"""
Unit tests for ParameterStore.

TASK-055-2
TASK-055-FIX: Removed tests for deleted methods (list_mappings, delete_mapping, get_stats).
"""

from typing import Any, Dict, List, Optional

import pytest
from server.router.application.resolver.parameter_store import ParameterStore
from server.router.domain.entities.parameter import StoredMapping
from server.router.domain.interfaces.i_vector_store import (
    IVectorStore,
    SearchResult,
    VectorNamespace,
    VectorRecord,
)
from server.router.domain.interfaces.i_workflow_intent_classifier import (
    IWorkflowIntentClassifier,
)


class MockClassifier(IWorkflowIntentClassifier):
    """Mock classifier for testing."""

    def __init__(self):
        self._embeddings: Dict[str, List[float]] = {}
        self._default_embedding = [0.1] * 768
        self._loaded = True

    def set_embedding(self, text: str, embedding: List[float]) -> None:
        """Set a specific embedding for a text."""
        self._embeddings[text] = embedding

    def get_embedding(self, text: str) -> List[float]:
        """Get embedding for text."""
        return self._embeddings.get(text, self._default_embedding)

    def similarity(self, text1: str, text2: str) -> float:
        """Calculate similarity between texts."""
        return 0.8

    def load_workflow_embeddings(self, workflows: Dict[str, Any]) -> None:
        """Load workflow embeddings."""
        pass

    def find_similar(
        self,
        prompt: str,
        top_k: int = 3,
        threshold: float = 0.0,
    ) -> List[tuple]:
        """Find similar workflows."""
        return []

    def find_best_match(
        self,
        prompt: str,
        min_confidence: float = 0.5,
    ) -> Optional[tuple]:
        """Find best matching workflow."""
        return None

    def get_generalization_candidates(
        self,
        prompt: str,
        min_similarity: float = 0.3,
        max_candidates: int = 3,
    ) -> List[tuple]:
        """Get generalization candidates."""
        return []

    def is_loaded(self) -> bool:
        """Check if loaded."""
        return self._loaded

    def get_info(self) -> Dict[str, Any]:
        """Get classifier info."""
        return {"mock": True}

    def clear_cache(self) -> bool:
        """Clear cache."""
        self._embeddings.clear()
        return True


class MockVectorStore(IVectorStore):
    """Mock vector store for testing."""

    def __init__(self):
        self._records: Dict[str, VectorRecord] = {}

    def upsert(self, records: List[VectorRecord]) -> int:
        """Insert or update records."""
        for record in records:
            key = f"{record.namespace.value}:{record.id}"
            self._records[key] = record
        return len(records)

    def search(
        self,
        query_vector: List[float],
        namespace: VectorNamespace,
        top_k: int = 5,
        threshold: float = 0.0,
        metadata_filter: Optional[Dict[str, Any]] = None,
    ) -> List[SearchResult]:
        """Search for similar vectors."""
        results = []

        for key, record in self._records.items():
            if record.namespace != namespace:
                continue

            # Apply metadata filter
            if metadata_filter:
                match = True
                for k, v in metadata_filter.items():
                    if record.metadata.get(k) != v:
                        match = False
                        break
                if not match:
                    continue

            # Mock similarity calculation
            score = 0.9  # High score for testing

            if score >= threshold:
                results.append(
                    SearchResult(
                        id=record.id,
                        score=score,
                        text=record.text,
                        metadata=record.metadata,
                    )
                )

        return results[:top_k]

    def delete(self, ids: List[str], namespace: VectorNamespace) -> int:
        """Delete records by IDs."""
        count = 0
        for id_ in ids:
            key = f"{namespace.value}:{id_}"
            if key in self._records:
                del self._records[key]
                count += 1
        return count

    def count(self, namespace: Optional[VectorNamespace] = None) -> int:
        """Count records in namespace."""
        if namespace is None:
            return len(self._records)
        return sum(1 for r in self._records.values() if r.namespace == namespace)

    def get_stats(self) -> Dict[str, Any]:
        """Get storage statistics."""
        return {"total": len(self._records)}

    def rebuild_index(self) -> bool:
        """Rebuild index."""
        return True

    def clear(self, namespace: Optional[VectorNamespace] = None) -> int:
        """Clear records."""
        if namespace is None:
            count = len(self._records)
            self._records.clear()
            return count

        to_delete = [key for key, r in self._records.items() if r.namespace == namespace]
        for key in to_delete:
            del self._records[key]
        return len(to_delete)

    def get_all_ids(self, namespace: VectorNamespace) -> List[str]:
        """Get all IDs in namespace."""
        return [r.id for r in self._records.values() if r.namespace == namespace]

    def search_workflows_weighted(
        self,
        query_vector: List[float],
        query_language: str = "en",
        top_k: int = 5,
        min_score: float = 0.5,
    ) -> List:
        """Search workflows weighted (not used in ParameterStore)."""
        return []

    def get_workflow_embedding_count(self) -> int:
        """Get workflow embedding count."""
        return 0

    def get_unique_workflow_count(self) -> int:
        """Get unique workflow count."""
        return 0


@pytest.fixture
def mock_classifier():
    """Create mock classifier."""
    return MockClassifier()


@pytest.fixture
def mock_vector_store():
    """Create mock vector store."""
    return MockVectorStore()


@pytest.fixture
def parameter_store(mock_classifier, mock_vector_store):
    """Create parameter store with mocks."""
    return ParameterStore(
        classifier=mock_classifier,
        vector_store=mock_vector_store,
        similarity_threshold=0.85,
    )


class TestParameterStoreBasics:
    """Basic tests for ParameterStore."""

    def test_initialization(self, parameter_store):
        """Test store initialization."""
        assert parameter_store is not None
        assert parameter_store._similarity_threshold == 0.85

    def test_store_mapping(self, parameter_store, mock_vector_store):
        """Test storing a parameter mapping."""
        parameter_store.store_mapping(
            context="prostymi nogami",
            parameter_name="leg_angle_left",
            value=0,
            workflow_name="picnic_table",
        )

        # Check that record was stored
        assert mock_vector_store.count(VectorNamespace.PARAMETERS) == 1

    def test_store_multiple_mappings(self, parameter_store, mock_vector_store):
        """Test storing multiple mappings."""
        parameter_store.store_mapping(
            context="straight legs",
            parameter_name="leg_angle_left",
            value=0,
            workflow_name="table",
        )
        parameter_store.store_mapping(
            context="angled legs",
            parameter_name="leg_angle_left",
            value=0.32,
            workflow_name="table",
        )
        parameter_store.store_mapping(
            context="wide top",
            parameter_name="top_width",
            value=2.0,
            workflow_name="table",
        )

        assert mock_vector_store.count(VectorNamespace.PARAMETERS) == 3


class TestParameterStoreFindMapping:
    """Tests for find_mapping functionality."""

    def test_find_existing_mapping(self, parameter_store):
        """Test finding an existing mapping."""
        # Store a mapping
        parameter_store.store_mapping(
            context="straight legs",
            parameter_name="leg_angle",
            value=0,
            workflow_name="table",
        )

        # Find it
        result = parameter_store.find_mapping(
            prompt="with straight legs",
            parameter_name="leg_angle",
            workflow_name="table",
        )

        assert result is not None
        assert result.parameter_name == "leg_angle"
        assert result.value == 0
        assert result.workflow_name == "table"

    def test_find_mapping_not_found(self, parameter_store):
        """Test finding when no mapping exists."""
        result = parameter_store.find_mapping(
            prompt="curved legs",
            parameter_name="leg_angle",
            workflow_name="table",
        )

        assert result is None

    def test_find_mapping_wrong_parameter(self, parameter_store):
        """Test that filtering by parameter works."""
        parameter_store.store_mapping(
            context="straight legs",
            parameter_name="leg_angle",
            value=0,
            workflow_name="table",
        )

        # Search for different parameter - should not find
        parameter_store.find_mapping(
            prompt="straight legs",
            parameter_name="top_width",  # Different parameter
            workflow_name="table",
        )

        # MockVectorStore doesn't properly filter, so this may find the record
        # In real implementation with proper filter, this would return None
        # For unit test purposes, we're testing the interface

    def test_find_mapping_with_custom_threshold(self, parameter_store):
        """Test finding with custom similarity threshold."""
        parameter_store.store_mapping(
            context="proste nogi",
            parameter_name="angle",
            value=0,
            workflow_name="table",
        )

        # With high threshold
        parameter_store.find_mapping(
            prompt="straight legs",
            parameter_name="angle",
            workflow_name="table",
            similarity_threshold=0.99,  # Very high
        )

        # With low threshold
        parameter_store.find_mapping(
            prompt="straight legs",
            parameter_name="angle",
            workflow_name="table",
            similarity_threshold=0.5,  # Low
        )

        # Both should work with mock that returns 0.9 score


class TestParameterStoreUsageTracking:
    """Tests for usage count tracking."""

    def test_increment_usage(self, parameter_store, mock_vector_store):
        """Test incrementing usage count."""
        # Store mapping
        parameter_store.store_mapping(
            context="test context",
            parameter_name="param",
            value=1,
            workflow_name="workflow",
        )

        # Create mapping object to increment
        mapping = StoredMapping(
            context="test context",
            value=1,
            similarity=0.9,
            workflow_name="workflow",
            parameter_name="param",
            usage_count=1,
        )

        # Increment usage
        parameter_store.increment_usage(mapping)

        # Check record was updated (still 1 record)
        assert mock_vector_store.count(VectorNamespace.PARAMETERS) == 1


class TestParameterStoreClear:
    """Tests for clearing mappings."""

    def test_clear_all(self, parameter_store, mock_vector_store):
        """Test clearing all mappings."""
        parameter_store.store_mapping("ctx1", "p1", 1, "wf1")
        parameter_store.store_mapping("ctx2", "p2", 2, "wf2")

        assert mock_vector_store.count(VectorNamespace.PARAMETERS) == 2

        deleted = parameter_store.clear()

        assert deleted == 2
        assert mock_vector_store.count(VectorNamespace.PARAMETERS) == 0


class TestParameterStoreRecordIdGeneration:
    """Tests for record ID generation."""

    def test_record_id_is_deterministic(self, parameter_store):
        """Test that same inputs produce same ID."""
        id1 = parameter_store._generate_record_id("ctx", "param", "wf")
        id2 = parameter_store._generate_record_id("ctx", "param", "wf")

        assert id1 == id2

    def test_record_id_differs_for_different_inputs(self, parameter_store):
        """Test that different inputs produce different IDs."""
        id1 = parameter_store._generate_record_id("ctx1", "param", "wf")
        id2 = parameter_store._generate_record_id("ctx2", "param", "wf")

        assert id1 != id2

    def test_record_id_format(self, parameter_store):
        """Test record ID format."""
        record_id = parameter_store._generate_record_id(
            "context",
            "leg_angle",
            "picnic_table",
        )

        assert record_id.startswith("param_picnic_table_leg_angle_")
        assert len(record_id) > len("param_picnic_table_leg_angle_")
