"""Layer 3: Execution — Black-Box Identity Behavior.

Proving: "Given known data + known spec, Kanoniv produces the expected identity graph."
Uses golden datasets. Requires native extension with reconcile_local.
"""
from __future__ import annotations

import pytest

from tests.contract.conftest import (
    CUSTOMER_SPEC,
    SINGLE_SOURCE_SPEC,
    make_source,
    make_spec,
    requires_reconcile,
)

pytestmark = requires_reconcile


# ---------------------------------------------------------------------------
# Core identity behavior
# ---------------------------------------------------------------------------


class TestCoreIdentityBehavior:
    def test_exact_email_merge(self, tmp_path):
        """Two records same email → merged into 1 cluster."""
        from kanoniv.reconcile import reconcile

        rows_a = [
            {"id": "1", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
        ]
        rows_b = [
            {"id": "2", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0002"},
        ]
        src_a = make_source(tmp_path, "crm", rows_a)
        src_b = make_source(tmp_path, "ecommerce", rows_b)
        spec = make_spec(CUSTOMER_SPEC)
        result = reconcile([src_a, src_b], spec)
        assert result.cluster_count == 1

    def test_no_overlap_no_merge(self, tmp_path):
        """Disjoint records → each stays singleton."""
        from kanoniv.reconcile import reconcile

        rows_a = [
            {"id": "1", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
        ]
        rows_b = [
            {"id": "2", "email": "bob@example.com", "first_name": "Bob",
             "last_name": "Jones", "phone": "555-0002"},
        ]
        src_a = make_source(tmp_path, "crm", rows_a)
        src_b = make_source(tmp_path, "ecommerce", rows_b)
        spec = make_spec(CUSTOMER_SPEC)
        result = reconcile([src_a, src_b], spec)
        assert result.cluster_count == 2

    def test_golden_records_count_equals_clusters(self, tmp_path):
        """len(golden_records) == cluster_count always."""
        from kanoniv.reconcile import reconcile

        rows_a = [
            {"id": "1", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
            {"id": "2", "email": "bob@example.com", "first_name": "Bob",
             "last_name": "Jones", "phone": "555-0002"},
        ]
        rows_b = [
            {"id": "3", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0003"},
        ]
        src_a = make_source(tmp_path, "crm", rows_a)
        src_b = make_source(tmp_path, "ecommerce", rows_b)
        spec = make_spec(CUSTOMER_SPEC)
        result = reconcile([src_a, src_b], spec)
        assert len(result.golden_records) == result.cluster_count

    def test_merge_rate_formula(self, tmp_path):
        """merge_rate == 1 - clusters/total_entities."""
        from kanoniv.reconcile import reconcile

        rows_a = [
            {"id": "1", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
        ]
        rows_b = [
            {"id": "2", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0002"},
        ]
        src_a = make_source(tmp_path, "crm", rows_a)
        src_b = make_source(tmp_path, "ecommerce", rows_b)
        spec = make_spec(CUSTOMER_SPEC)
        result = reconcile([src_a, src_b], spec)
        total = sum(len(c) for c in result.clusters)
        expected = 1.0 - (result.cluster_count / total)
        assert abs(result.merge_rate - expected) < 1e-9

    def test_decisions_contain_merge(self, tmp_path):
        """At least one merge decision when overlap exists."""
        from kanoniv.reconcile import reconcile

        rows_a = [
            {"id": "1", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
        ]
        rows_b = [
            {"id": "2", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
        ]
        src_a = make_source(tmp_path, "crm", rows_a)
        src_b = make_source(tmp_path, "ecommerce", rows_b)
        spec = make_spec(CUSTOMER_SPEC)
        result = reconcile([src_a, src_b], spec)
        decisions = result.decisions
        has_merge = any(
            d.get("decision", d.get("type", "")) in ("merge", "match")
            for d in decisions
        )
        assert has_merge

    def test_multi_source_cross_link(self, tmp_path):
        """Email match across 2 sources → cross-source cluster."""
        from kanoniv.reconcile import reconcile

        rows_a = [
            {"id": "1", "email": "shared@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
        ]
        rows_b = [
            {"id": "2", "email": "shared@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0002"},
        ]
        src_a = make_source(tmp_path, "crm", rows_a)
        src_b = make_source(tmp_path, "ecommerce", rows_b)
        spec = make_spec(CUSTOMER_SPEC)
        result = reconcile([src_a, src_b], spec)
        assert result.cluster_count == 1
        assert len(result.clusters[0]) == 2

    def test_five_unique_records_five_clusters(self, tmp_path):
        """5 distinct records → 5 clusters."""
        from kanoniv.reconcile import reconcile

        rows = [
            {"id": str(i), "email": f"user{i}@example.com",
             "first_name": f"User{i}", "last_name": f"Last{i}",
             "phone": f"555-{i:04d}"}
            for i in range(1, 6)
        ]
        src = make_source(tmp_path, "main", rows)
        spec = make_spec(SINGLE_SOURCE_SPEC)
        result = reconcile([src], spec)
        assert result.cluster_count == 5

    def test_all_identical_records_one_cluster(self, tmp_path):
        """5 records same email → 1 cluster."""
        from kanoniv.reconcile import reconcile

        rows = [
            {"id": str(i), "email": "same@example.com",
             "first_name": "Same", "last_name": "Person",
             "phone": "555-9999"}
            for i in range(1, 6)
        ]
        src = make_source(tmp_path, "main", rows)
        spec = make_spec(SINGLE_SOURCE_SPEC)
        result = reconcile([src], spec)
        assert result.cluster_count == 1

    def test_mixed_match_review_reject(self, tmp_path):
        """Data that spans match, review, and reject zones."""
        from kanoniv.reconcile import reconcile

        rows = [
            {"id": "1", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0001"},
            {"id": "2", "email": "alice@example.com", "first_name": "Alice",
             "last_name": "Smith", "phone": "555-0002"},
            {"id": "3", "email": "bob@other.com", "first_name": "Bob",
             "last_name": "Jones", "phone": "555-9999"},
        ]
        src = make_source(tmp_path, "main", rows)
        spec = make_spec(SINGLE_SOURCE_SPEC)
        result = reconcile([src], spec)
        assert result.cluster_count >= 2
