"""Deduplication / merge tests — Task 5.23."""

from __future__ import annotations

import pytest
from httpx import AsyncClient

from tests.conftest import ORIGIN_HEADER, login_client, seed_tenant_and_users


async def _get_csrf_token(client: AsyncClient) -> str:
    """Extract CSRF token from login response."""
    # The login already happened, but we need the token from the response.
    # Re-login to get a fresh token (or extract from the last response).
    resp = await client.post(
        "/api/v1/auth/login",
        json={"email": "admin@tenanta.com", "password": "TestPass123!"},
        headers=ORIGIN_HEADER,
    )
    return resp.json().get("csrf_token", "")


def _csrf_headers(token: str) -> dict:
    """Return headers with CSRF token."""
    headers = dict(ORIGIN_HEADER)
    headers["X-CSRF-Token"] = token
    return headers


@pytest.mark.asyncio
class TestFindDuplicates:
    """POST /api/v1/contacts/duplicates — find duplicate contacts."""

    async def test_find_duplicates_by_email(self, client: AsyncClient, db_session):
        """Two contacts with same email should be detected as duplicates."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        csrf = await _get_csrf_token(client)
        hdrs = _csrf_headers(csrf)

        # Create two contacts with same email
        resp1 = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "John", "surname": "Doe", "email_1": "john@example.com"},
            headers=hdrs,
        )
        assert resp1.status_code == 201, f"Create 1 failed: {resp1.text}"

        resp2 = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "Johnny", "surname": "Doe", "email_1": "john@example.com"},
            headers=hdrs,
        )
        assert resp2.status_code == 201, f"Create 2 failed: {resp2.text}"

        # Find duplicates
        resp = await client.post(
            "/api/v1/contacts/duplicates",
            json={"threshold": 0.5, "limit": 50},
            headers=hdrs,
        )
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        assert len(data) >= 1
        pair = data[0]
        assert "source_contact" in pair
        assert "target_contact" in pair
        assert "similarity_score" in pair
        assert "match_reasons" in pair
        assert "email_match" in pair["match_reasons"]

    async def test_find_duplicates_empty(self, client: AsyncClient, db_session):
        """No duplicates when contacts are unique."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        csrf = await _get_csrf_token(client)
        hdrs = _csrf_headers(csrf)

        resp = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "Unique", "surname": "Person", "email_1": "unique@example.com"},
            headers=hdrs,
        )
        assert resp.status_code == 201, f"Create failed: {resp.text}"

        resp = await client.post(
            "/api/v1/contacts/duplicates",
            json={"threshold": 0.7, "limit": 50},
            headers=hdrs,
        )
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        assert len(data) == 0


@pytest.mark.asyncio
class TestMergeContacts:
    """POST /api/v1/contacts/merge — merge two contacts."""

    async def test_merge_contacts_success(self, client: AsyncClient, db_session):
        """Merge source into target, source should be soft-deleted."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        csrf = await _get_csrf_token(client)
        hdrs = _csrf_headers(csrf)

        # Create source contact with phone
        resp1 = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "Source", "surname": "Test", "email_1": "source@example.com", "phone_1": "+49123456789"},
            headers=hdrs,
        )
        assert resp1.status_code == 201, f"Create source failed: {resp1.text}"
        source_id = resp1.json()["id"]

        # Create target contact without phone
        resp2 = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "Target", "surname": "Test", "email_1": "target@example.com"},
            headers=hdrs,
        )
        assert resp2.status_code == 201, f"Create target failed: {resp2.text}"
        target_id = resp2.json()["id"]

        # Merge
        resp = await client.post(
            "/api/v1/contacts/merge",
            json={"source_contact_id": source_id, "target_contact_id": target_id},
            headers=hdrs,
        )
        assert resp.status_code == 200, f"Merge failed: {resp.text}"
        data = resp.json()
        assert data["source_contact_id"] == source_id
        assert data["target_contact_id"] == target_id
        assert "merge_id" in data
        assert "merged_fields" in data
        # Phone should have been auto-merged
        assert "phone_1" in data["merged_fields"]

        # Source should be soft-deleted (not in list)
        list_resp = await client.get("/api/v1/contacts", headers=ORIGIN_HEADER)
        contact_ids = [c["id"] for c in list_resp.json()["items"]]
        assert source_id not in contact_ids
        assert target_id in contact_ids

    async def test_merge_same_contact_fails(self, client: AsyncClient, db_session):
        """Merging a contact with itself should fail."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        csrf = await _get_csrf_token(client)
        hdrs = _csrf_headers(csrf)

        resp = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "Same", "surname": "Contact", "email_1": "same@example.com"},
            headers=hdrs,
        )
        assert resp.status_code == 201, f"Create failed: {resp.text}"
        contact_id = resp.json()["id"]

        resp = await client.post(
            "/api/v1/contacts/merge",
            json={"source_contact_id": contact_id, "target_contact_id": contact_id},
            headers=hdrs,
        )
        assert resp.status_code == 400


@pytest.mark.asyncio
class TestMergeHistory:
    """GET /api/v1/contacts/merge-history — merge history."""

    async def test_merge_history_returns_records(self, client: AsyncClient, db_session):
        """After a merge, history should contain the record."""
        await seed_tenant_and_users(db_session)
        await login_client(client, "admin@tenanta.com")
        csrf = await _get_csrf_token(client)
        hdrs = _csrf_headers(csrf)

        # Create and merge two contacts
        resp1 = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "Hist", "surname": "Source", "email_1": "hist-source@example.com"},
            headers=hdrs,
        )
        assert resp1.status_code == 201
        source_id = resp1.json()["id"]

        resp2 = await client.post(
            "/api/v1/contacts",
            json={"type": "person", "firstname": "Hist", "surname": "Target", "email_1": "hist-target@example.com"},
            headers=hdrs,
        )
        assert resp2.status_code == 201
        target_id = resp2.json()["id"]

        merge_resp = await client.post(
            "/api/v1/contacts/merge",
            json={"source_contact_id": source_id, "target_contact_id": target_id, "note": "Test merge"},
            headers=hdrs,
        )
        assert merge_resp.status_code == 200

        # Get history (GET doesn't need CSRF)
        resp = await client.get("/api/v1/contacts/merge-history", headers=ORIGIN_HEADER)
        assert resp.status_code == 200
        data = resp.json()
        assert "items" in data
        assert "total" in data
        assert data["total"] >= 1
        item = data["items"][0]
        assert item["source_contact_id"] == source_id
        assert item["target_contact_id"] == target_id
        assert item["note"] == "Test merge"
