"""Integration tests for the Data Legion Python SDK.

These tests call the real Data Legion API. Set the DATALEGION_API_KEY
environment variable to run them.
"""

from __future__ import annotations

import os

import pytest

from datalegion import (
    AsyncDataLegion,
    BulkCompanyEnrichResponse,
    BulkPersonEnrichResponse,
    DataLegion,
    EmailHashResponse,
    FieldCleanResponse,
    HealthResponse,
    PersonMatchesResponse,
    PersonResponse,
    ValidationResponse,
)
from datalegion._exceptions import AuthenticationError

API_KEY = os.environ.get("DATALEGION_API_KEY", "")

requires_api_key = pytest.mark.skipif(not API_KEY, reason="No DATALEGION_API_KEY set")
integration = pytest.mark.integration

LINKEDIN_URL = "https://www.linkedin.com/in/jfarza1/"


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def client():
    """Create a sync client for the test session."""
    c = DataLegion(api_key=API_KEY)
    yield c
    c.close()


@pytest.fixture
async def async_client():
    """Create an async client for the test session."""
    c = AsyncDataLegion(api_key=API_KEY)
    yield c
    await c.close()


# ---------------------------------------------------------------------------
# Client initialization
# ---------------------------------------------------------------------------


class TestClientInit:
    """Tests for client initialization (no API call needed)."""

    def test_missing_api_key_raises(self, monkeypatch):
        """Creating a client without an API key raises AuthenticationError."""
        monkeypatch.delenv("DATALEGION_API_KEY", raising=False)
        with pytest.raises(AuthenticationError):
            DataLegion(api_key="")

    def test_missing_api_key_async_raises(self, monkeypatch):
        """Creating an async client without an API key raises AuthenticationError."""
        monkeypatch.delenv("DATALEGION_API_KEY", raising=False)
        with pytest.raises(AuthenticationError):
            AsyncDataLegion(api_key="")

    @requires_api_key
    def test_explicit_api_key(self):
        """Explicit api_key argument works."""
        c = DataLegion(api_key=API_KEY)
        assert c._api_key == API_KEY
        c.close()

    @requires_api_key
    def test_env_var_api_key(self, monkeypatch):
        """Client picks up API key from environment variable."""
        monkeypatch.setenv("DATALEGION_API_KEY", API_KEY)
        c = DataLegion()
        assert c._api_key == API_KEY
        c.close()

    @requires_api_key
    def test_context_manager(self):
        """Client works as a context manager."""
        with DataLegion(api_key=API_KEY) as c:
            assert c._api_key == API_KEY

    @requires_api_key
    async def test_async_context_manager(self):
        """Async client works as a context manager."""
        async with AsyncDataLegion(api_key=API_KEY) as c:
            assert c._api_key == API_KEY


# ---------------------------------------------------------------------------
# Error handling
# ---------------------------------------------------------------------------


class TestErrorHandling:
    """Tests for error handling."""

    def test_bad_api_key_raises_authentication_error(self):
        """A request with a bad API key raises AuthenticationError."""
        c = DataLegion(api_key="legion_invalid_key_000000000000")
        with pytest.raises(AuthenticationError) as exc_info:
            c.person.enrich(social_url=LINKEDIN_URL)
        assert exc_info.value.status_code == 401
        c.close()

    async def test_async_bad_api_key_raises_authentication_error(self):
        """An async request with a bad API key raises AuthenticationError."""
        c = AsyncDataLegion(api_key="legion_invalid_key_000000000000")
        with pytest.raises(AuthenticationError) as exc_info:
            await c.person.enrich(social_url=LINKEDIN_URL)
        assert exc_info.value.status_code == 401
        await c.close()


# ---------------------------------------------------------------------------
# Person endpoints
# ---------------------------------------------------------------------------


@requires_api_key
@integration
class TestPersonEnrich:
    """Tests for person enrichment."""

    def test_enrich_by_social_url(self, client):
        """Enrich a person by LinkedIn URL returns a PersonResponse."""
        result = client.person.enrich(social_url=LINKEDIN_URL)
        assert isinstance(result, PersonResponse)
        assert isinstance(result.legion_id, str)
        assert result.full_name is not None
        assert result.first_name is not None
        assert result.last_name is not None

    def test_enrich_multiple_results(self, client):
        """Enrich with multiple_results returns a PersonMatchesResponse."""
        result = client.person.enrich(social_url=LINKEDIN_URL, multiple_results=True)
        assert isinstance(result, PersonMatchesResponse)
        assert isinstance(result.total, int)
        assert isinstance(result.matches, list)
        assert len(result.matches) > 0
        assert isinstance(result.matches[0].person, PersonResponse)

    def test_enrich_response_headers(self, client):
        """Response headers are populated after a request."""
        client.person.enrich(social_url=LINKEDIN_URL)
        assert client.request_id is not None
        assert isinstance(client.request_id, str)
        assert len(client.request_id) > 0

    async def test_async_enrich_by_social_url(self, async_client):
        """Async enrich a person by LinkedIn URL returns a PersonResponse."""
        result = await async_client.person.enrich(social_url=LINKEDIN_URL)
        assert isinstance(result, PersonResponse)
        assert isinstance(result.legion_id, str)
        assert result.full_name is not None

    async def test_async_enrich_response_headers(self, async_client):
        """Async response headers are populated after a request."""
        await async_client.person.enrich(social_url=LINKEDIN_URL)
        assert async_client.request_id is not None
        assert isinstance(async_client.request_id, str)


@requires_api_key
@integration
class TestPersonSearch:
    """Tests for person search."""

    def test_search_returns_matches(self, client):
        """Person search returns a PersonMatchesResponse."""
        result = client.person.search(
            query="SELECT * FROM people WHERE job_title ILIKE '%engineer%' AND company_name ILIKE '%google%'",
            limit=1,
        )
        assert isinstance(result, PersonMatchesResponse)
        assert isinstance(result.matches, list)
        assert isinstance(result.total, int)
        if result.matches:
            assert isinstance(result.matches[0].person, PersonResponse)

    async def test_async_search_returns_matches(self, async_client):
        """Async person search returns a PersonMatchesResponse."""
        result = await async_client.person.search(
            query="SELECT * FROM people WHERE job_title ILIKE '%engineer%' AND company_name ILIKE '%google%'",
            limit=1,
        )
        assert isinstance(result, PersonMatchesResponse)
        assert isinstance(result.matches, list)


@requires_api_key
@integration
class TestPersonDiscover:
    """Tests for person discover."""

    def test_discover_returns_matches(self, client):
        """Person discover returns a PersonMatchesResponse."""
        result = client.person.discover(query="find software engineers at google", limit=1)
        assert isinstance(result, PersonMatchesResponse)
        assert isinstance(result.matches, list)
        assert isinstance(result.total, int)
        if result.matches:
            assert isinstance(result.matches[0].person, PersonResponse)

    async def test_async_discover_returns_matches(self, async_client):
        """Async person discover returns a PersonMatchesResponse."""
        result = await async_client.person.discover(
            query="find software engineers at google", limit=1
        )
        assert isinstance(result, PersonMatchesResponse)
        assert isinstance(result.matches, list)


@requires_api_key
@integration
class TestPersonBulkEnrich:
    """Tests for person bulk enrichment."""

    def test_bulk_enrich_returns_results(self, client):
        """Bulk person enrich returns a BulkPersonEnrichResponse."""
        result = client.person.bulk_enrich(
            items=[{"social_url": LINKEDIN_URL}],
        )
        assert isinstance(result, BulkPersonEnrichResponse)
        assert isinstance(result.results, list)
        assert len(result.results) > 0

    async def test_async_bulk_enrich_returns_results(self, async_client):
        """Async bulk person enrich returns a BulkPersonEnrichResponse."""
        result = await async_client.person.bulk_enrich(
            items=[{"social_url": LINKEDIN_URL}],
        )
        assert isinstance(result, BulkPersonEnrichResponse)
        assert isinstance(result.results, list)
        assert len(result.results) > 0


# ---------------------------------------------------------------------------
# Company endpoints (skipped — not yet available)
# ---------------------------------------------------------------------------


@pytest.mark.skip(reason="Company endpoints not yet available")
@requires_api_key
@integration
class TestCompanyEnrich:
    """Tests for company enrichment."""

    def test_enrich_by_domain(self, client):
        """Enrich a company by domain returns expected fields."""
        result = client.company.enrich(domain="google.com")
        assert isinstance(result.legion_id, str)

    async def test_async_enrich_by_domain(self, async_client):
        """Async enrich a company by domain returns expected fields."""
        result = await async_client.company.enrich(domain="google.com")
        assert isinstance(result.legion_id, str)


@pytest.mark.skip(reason="Company endpoints not yet available")
@requires_api_key
@integration
class TestCompanyBulkEnrich:
    """Tests for company bulk enrichment."""

    def test_bulk_enrich_returns_results(self, client):
        """Bulk company enrich returns a BulkCompanyEnrichResponse."""
        result = client.company.bulk_enrich(
            items=[{"domain": "google.com"}],
        )
        assert isinstance(result, BulkCompanyEnrichResponse)
        assert isinstance(result.results, list)
        assert len(result.results) > 0

    async def test_async_bulk_enrich_returns_results(self, async_client):
        """Async bulk company enrich returns a BulkCompanyEnrichResponse."""
        result = await async_client.company.bulk_enrich(
            items=[{"domain": "google.com"}],
        )
        assert isinstance(result, BulkCompanyEnrichResponse)
        assert isinstance(result.results, list)
        assert len(result.results) > 0


@pytest.mark.skip(reason="Company endpoints not yet available")
@requires_api_key
@integration
class TestCompanySearch:
    """Tests for company search."""

    def test_search_returns_matches(self, client):
        """Company search returns matches list."""
        result = client.company.search(
            query="SELECT * FROM companies WHERE name ILIKE '%google%'",
            limit=1,
        )
        assert isinstance(result.matches, list)

    async def test_async_search_returns_matches(self, async_client):
        """Async company search returns matches list."""
        result = await async_client.company.search(
            query="SELECT * FROM companies WHERE name ILIKE '%google%'",
            limit=1,
        )
        assert isinstance(result.matches, list)


@pytest.mark.skip(reason="Company endpoints not yet available")
@requires_api_key
@integration
class TestCompanyDiscover:
    """Tests for company discover."""

    def test_discover_returns_matches(self, client):
        """Company discover returns matches list."""
        result = client.company.discover(query="find tech companies", limit=1)
        assert isinstance(result.matches, list)

    async def test_async_discover_returns_matches(self, async_client):
        """Async company discover returns matches list."""
        result = await async_client.company.discover(query="find tech companies", limit=1)
        assert isinstance(result.matches, list)


# ---------------------------------------------------------------------------
# Utility endpoints
# ---------------------------------------------------------------------------


@requires_api_key
@integration
class TestUtilityClean:
    """Tests for utility clean endpoint."""

    def test_clean_email(self, client):
        """Clean normalizes an email address."""
        result = client.utility.clean(fields={"email": "  JOHN@EXAMPLE.COM  "})
        assert isinstance(result, FieldCleanResponse)
        assert "email" in result.results
        email_result = result.results["email"]
        assert email_result.original == "  JOHN@EXAMPLE.COM  "
        assert email_result.cleaned is not None

    async def test_async_clean_email(self, async_client):
        """Async clean normalizes an email address."""
        result = await async_client.utility.clean(fields={"email": "  JOHN@EXAMPLE.COM  "})
        assert isinstance(result, FieldCleanResponse)
        assert "email" in result.results


@requires_api_key
@integration
class TestUtilityHashEmail:
    """Tests for utility hash email endpoint."""

    def test_hash_email(self, client):
        """Hash email returns expected hash fields."""
        result = client.utility.hash_email(email="jon@datalegion.ai")
        assert isinstance(result, EmailHashResponse)
        assert result.email == "jon@datalegion.ai"
        assert result.normalized_email is not None
        assert isinstance(result.hashes, dict)
        assert "sha256" in result.hashes
        assert "sha1" in result.hashes
        assert "md5" in result.hashes

    async def test_async_hash_email(self, async_client):
        """Async hash email returns expected hash fields."""
        result = await async_client.utility.hash_email(email="jon@datalegion.ai")
        assert isinstance(result, EmailHashResponse)
        assert result.email == "jon@datalegion.ai"
        assert isinstance(result.hashes, dict)


@requires_api_key
@integration
class TestUtilityValidate:
    """Tests for utility validate endpoint."""

    def test_validate_email(self, client):
        """Validate returns expected validation structure."""
        result = client.utility.validate(email="jon@datalegion.ai")
        assert isinstance(result, ValidationResponse)
        assert isinstance(result.valid, bool)
        assert isinstance(result.errors, list)
        assert isinstance(result.warnings, list)
        assert isinstance(result.suggestions, list)

    async def test_async_validate_email(self, async_client):
        """Async validate returns expected validation structure."""
        result = await async_client.utility.validate(email="jon@datalegion.ai")
        assert isinstance(result, ValidationResponse)
        assert isinstance(result.valid, bool)
        assert isinstance(result.errors, list)
        assert isinstance(result.warnings, list)
        assert isinstance(result.suggestions, list)


# ---------------------------------------------------------------------------
# Health endpoint
# ---------------------------------------------------------------------------


@requires_api_key
@integration
class TestHealth:
    """Tests for the health endpoint."""

    def test_health(self, client):
        """Health endpoint returns a HealthResponse."""
        result = client.health()
        assert isinstance(result, HealthResponse)
        assert result.status == "healthy"

    async def test_async_health(self, async_client):
        """Async health endpoint returns a HealthResponse."""
        result = await async_client.health()
        assert isinstance(result, HealthResponse)
        assert result.status == "healthy"
