"""Tests for QueryIntentDetector."""

import pytest
import numpy as np
from insa_its.detectors.query_intent import QueryIntentDetector


class TestInit:
    def test_default_threshold(self):
        d = QueryIntentDetector()
        assert d.similarity_threshold == 0.65

    def test_custom_threshold(self):
        d = QueryIntentDetector(similarity_threshold=0.8)
        assert d.similarity_threshold == 0.8

    def test_invalid_threshold_raises(self):
        with pytest.raises(ValueError, match="similarity_threshold"):
            QueryIntentDetector(similarity_threshold=0.0)
        with pytest.raises(ValueError, match="similarity_threshold"):
            QueryIntentDetector(similarity_threshold=1.5)

    def test_name_and_enabled(self):
        d = QueryIntentDetector()
        assert d.name == "query_intent"
        assert d.enabled is True


class TestEntityMismatch:
    def test_detects_entity_mismatch(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show sales by region for each customer",
            query_text="SELECT category, SUM(amount) FROM orders GROUP BY category",
            message_idx=0,
        )
        assert result["detected"] is True
        # "region" and "customer" from intent are missing in query
        assert len(result["entity_mismatches"]) > 0
        assert any("Entity" in f for f in result["flags"])

    def test_no_mismatch_when_aligned(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show sales by region",
            query_text="SELECT region, SUM(sales) FROM orders GROUP BY region",
            message_idx=0,
        )
        # "region" and "sales" are in both
        assert "region" not in result["entity_mismatches"]
        assert "sales" not in result["entity_mismatches"]

    def test_stopwords_filtered(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show me the data",
            query_text="SELECT * FROM data",
            message_idx=0,
        )
        # "show", "me", "the" are stopwords, shouldn't cause mismatches
        # "data" appears in both
        assert "data" not in result["entity_mismatches"]


class TestAggregationMismatch:
    def test_detects_aggregation_mismatch(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show the average revenue per region",
            query_text="SELECT region, SUM(revenue) FROM sales GROUP BY region",
            message_idx=0,
        )
        assert result["detected"] is True
        assert result["aggregation_mismatch"] is not None
        assert "average" in result["aggregation_mismatch"].lower()

    def test_no_mismatch_when_aggregation_matches(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show the total revenue",
            query_text="SELECT SUM(revenue) FROM sales",
            message_idx=0,
        )
        # "total" normalizes to "sum", query has SUM -- should match
        assert result["aggregation_mismatch"] is None

    def test_detects_missing_aggregation(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Calculate the average price per category",
            query_text="SELECT category, price FROM products",
            message_idx=0,
        )
        assert result["aggregation_mismatch"] is not None
        assert "missing" in result["aggregation_mismatch"].lower()

    def test_avg_average_mean_normalized(self):
        d = QueryIntentDetector()
        # "average" in intent, "AVG" in query -- should match
        result = d.check(
            intent_text="Show the average price",
            query_text="SELECT AVG(price) FROM products",
            message_idx=0,
        )
        assert result["aggregation_mismatch"] is None


class TestFilterDetection:
    def test_detects_missing_date_filter(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show revenue for the last 30 days",
            query_text="SELECT SUM(revenue) FROM sales",
            message_idx=0,
        )
        assert result["detected"] is True
        assert len(result["missing_filters"]) > 0
        assert any("date" in f.lower() or "Date" in f for f in result["missing_filters"])

    def test_no_flag_when_date_filter_present(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show revenue for the last 30 days",
            query_text="SELECT SUM(revenue) FROM sales WHERE created_at > DATE_SUB(NOW(), INTERVAL 30 DAY)",
            message_idx=0,
        )
        # Query has DATE_SUB and NOW() -- should not flag
        date_flags = [f for f in result["missing_filters"] if "date" in f.lower() or "Date" in f]
        assert len(date_flags) == 0

    def test_detects_missing_exclusion_filter(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show all products excluding discontinued items",
            query_text="SELECT * FROM products",
            message_idx=0,
        )
        assert result["detected"] is True
        assert any("Filter omitted" in f or "filter" in f.lower() for f in result["flags"])


class TestScopeDetection:
    def test_detects_missing_scope(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show top 5 customers by revenue",
            query_text="SELECT customer, SUM(revenue) FROM orders GROUP BY customer",
            message_idx=0,
        )
        assert result["detected"] is True
        assert result["scope_mismatch"] is not None
        assert "top 5" in result["scope_mismatch"].lower()

    def test_no_flag_when_scope_present(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show top 5 customers by revenue",
            query_text="SELECT customer, SUM(revenue) FROM orders GROUP BY customer ORDER BY 2 DESC LIMIT 5",
            message_idx=0,
        )
        assert result["scope_mismatch"] is None

    def test_limit_pattern_detected(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show only 10 results",
            query_text="SELECT * FROM data LIMIT 10",
            message_idx=0,
        )
        assert result["scope_mismatch"] is None


class TestSortDetection:
    def test_detects_missing_sort(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show products sorted by price descending",
            query_text="SELECT * FROM products",
            message_idx=0,
        )
        assert result["detected"] is True
        assert result["sort_mismatch"] is not None
        assert "sort" in result["sort_mismatch"].lower()

    def test_no_flag_when_sort_present(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show products sorted by price",
            query_text="SELECT * FROM products ORDER BY price",
            message_idx=0,
        )
        assert result["sort_mismatch"] is None


class TestEmbeddingSimilarity:
    def test_no_similarity_without_embed_fn(self):
        d = QueryIntentDetector()
        result = d.check("Show sales", "SELECT * FROM sales", 0)
        assert result["similarity_score"] is None

    def test_low_similarity_flagged(self):
        def mock_embed(text):
            if "sales" in text.lower():
                return np.array([1.0, 0.0, 0.0])
            return np.array([0.0, 1.0, 0.0])

        d = QueryIntentDetector(similarity_threshold=0.8, embed_fn=mock_embed)
        result = d.check(
            intent_text="Show user profiles",
            query_text="DELETE FROM sales WHERE id > 0",
            message_idx=0,
        )
        assert result["similarity_score"] is not None
        assert result["similarity_score"] < 0.8
        assert any("similarity" in f.lower() for f in result["flags"])

    def test_high_similarity_not_flagged(self):
        def mock_embed(text):
            return np.array([1.0, 0.5, 0.2])

        d = QueryIntentDetector(similarity_threshold=0.5, embed_fn=mock_embed)
        result = d.check("Show sales", "SELECT sales FROM orders", 0)
        assert result["similarity_score"] is not None
        # Same embedding -> similarity = 1.0
        similarity_flags = [f for f in result["flags"] if "similarity" in f.lower()]
        assert len(similarity_flags) == 0

    def test_embed_fn_exception_handled(self):
        def broken_embed(text):
            raise RuntimeError("embedding failed")

        d = QueryIntentDetector(embed_fn=broken_embed)
        result = d.check("Show data", "SELECT * FROM data", 0)
        assert result["similarity_score"] is None


class TestReset:
    def test_reset_clears_history(self):
        d = QueryIntentDetector()
        d.check("Show sales by region", "SELECT * FROM sales", 0)
        assert len(d._history) == 1
        d.reset()
        assert len(d._history) == 0


class TestResultFormat:
    def test_result_has_all_keys(self):
        d = QueryIntentDetector()
        result = d.check("Show data", "SELECT * FROM data", 0)
        expected_keys = {
            "detected", "entity_mismatches", "aggregation_mismatch",
            "missing_filters", "scope_mismatch", "sort_mismatch",
            "similarity_score", "flags", "description",
        }
        assert expected_keys.issubset(set(result.keys()))

    def test_clean_description(self):
        d = QueryIntentDetector()
        result = d.check("Show data", "SELECT * FROM data", 0)
        if not result["detected"]:
            assert "No query-intent divergence" in result["description"]

    def test_flagged_description(self):
        d = QueryIntentDetector()
        result = d.check(
            "Show average revenue by region for the last 30 days sorted by revenue descending top 5",
            "SELECT * FROM sales",
            0,
        )
        assert result["detected"] is True
        assert "divergence" in result["description"].lower()


class TestComplexScenarios:
    def test_multiple_issues_detected(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show the average revenue by region for the last 30 days, top 5, sorted by revenue",
            query_text="SELECT category, SUM(amount) FROM orders",
            message_idx=0,
        )
        assert result["detected"] is True
        # Should have multiple flags: entity mismatch, aggregation, date filter, scope, sort
        assert len(result["flags"]) >= 3

    def test_clean_aligned_query(self):
        d = QueryIntentDetector()
        result = d.check(
            intent_text="Show total revenue by region",
            query_text="SELECT region, SUM(revenue) FROM sales GROUP BY region",
            message_idx=0,
        )
        # This should be mostly clean (region and revenue match, sum matches total)
        entity_flags = [f for f in result["flags"] if "Entity" in f]
        agg_flags = [f for f in result["flags"] if "Aggregation" in f]
        assert len(entity_flags) == 0
        assert len(agg_flags) == 0
