"""
Apollo.io enrichment client.

Uses the Apollo v1 enrichment endpoints to fetch:
- Company metadata by domain (/v1/companies/enrich)
- Contact/person metadata by email or name+company (/v1/people/match)

API key is read via resolve_credentials (env/keychain/unified config).
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional

import click

from g_gremlin.base import AuthenticationError, BaseIntegration, IntegrationError, RateLimitError, RetryConfig
from g_gremlin.credentials import resolve_credentials
from g_gremlin.credentials.provider import CredentialProvider, SecretNotFoundError

COMPANY_ENRICH_URL = "https://api.apollo.io/v1/companies/enrich"
PEOPLE_MATCH_URL = "https://api.apollo.io/v1/people/match"
PEOPLE_BULK_MATCH_URL = "https://api.apollo.io/v1/people/bulk_match"
PEOPLE_SEARCH_ENDPOINTS = (
    "https://api.apollo.io/api/v1/mixed_people/api_search",
    "https://api.apollo.io/v1/mixed_people/api_search",
    "https://api.apollo.io/api/v1/mixed_people/search",
    "https://api.apollo.io/v1/mixed_people/search",
)
COMPANY_SEARCH_ENDPOINTS = (
    "https://api.apollo.io/api/v1/organizations/search",
    "https://api.apollo.io/v1/organizations/search",
)
ENRICH_URL = COMPANY_ENRICH_URL  # Alias for backwards compat
DEFAULT_RETRY_CONFIG = RetryConfig(max_attempts=4, base_delay=1.2, max_delay=10.0)


class ApolloError(RuntimeError):
    """Raised when Apollo enrichment fails."""


def get_apollo_api_key(credential_provider: CredentialProvider | None = None) -> str:
    provider = credential_provider or CredentialProvider()
    try:
        creds = resolve_credentials("apollo", required_keys=["api_key"], credential_provider=provider)
        return creds["api_key"]
    except SecretNotFoundError:
        raise click.ClickException(
            "Apollo not configured. Set up with:\n"
            "  g-gremlin auth set apollo                    # Store in keychain\n"
            "  export G_GREMLIN_APOLLO_API_KEY=your-key     # Or use env var"
        )


def _extract_fields(company: Dict[str, Any]) -> Dict[str, Any]:
    if not company:
        return {}
    return {
        "name": company.get("name")
        or company.get("organization_name")
        or company.get("company_name"),
        "domain": company.get("domain") or company.get("website_url") or company.get("website"),
        "industry": company.get("industry"),
        "employee_count": company.get("employee_count") or company.get("estimated_num_employees"),
        "linkedin_url": company.get("linkedin_url") or company.get("linkedin"),
        "founded_year": company.get("founded_year") or company.get("founded"),
    }


def _extract_contact_fields(person: Dict[str, Any]) -> Dict[str, Any]:
    """Extract normalized fields from Apollo person response."""
    if not person:
        return {}

    org = person.get("organization") or {}

    return {
        "id": person.get("id"),
        "email": person.get("email"),
        "first_name": person.get("first_name"),
        "last_name": person.get("last_name"),
        "name": person.get("name"),
        "title": person.get("title"),
        "headline": person.get("headline"),
        "linkedin_url": person.get("linkedin_url"),
        "phone": person.get("phone_number") or person.get("sanitized_phone"),
        "city": person.get("city"),
        "state": person.get("state"),
        "country": person.get("country"),
        # Organization fields
        "company": org.get("name") or person.get("organization_name"),
        "company_domain": org.get("primary_domain") or person.get("domain"),
        "company_linkedin_url": org.get("linkedin_url"),
        "company_industry": org.get("industry"),
        "company_employee_count": org.get("estimated_num_employees"),
    }


class ApolloClient(BaseIntegration):
    SERVICE_NAME = "apollo"

    def __init__(
        self,
        api_key: Optional[str] = None,
        credential_provider: CredentialProvider | None = None,
        retry_config: RetryConfig | None = None,
    ) -> None:
        super().__init__(credential_provider=credential_provider, default_retry_config=retry_config or DEFAULT_RETRY_CONFIG)
        self.api_key = api_key or get_apollo_api_key(credential_provider)

    def _headers(self) -> Dict[str, str]:
        return {"Content-Type": "application/json", "x-api-key": self.api_key}

    def _is_endpoint_deprecation_response(self, response: Any) -> bool:
        if int(getattr(response, "status_code", 0)) != 422:
            return False
        try:
            body = (response.text or "").lower()
        except Exception:
            body = ""
        return "deprecated" in body and "endpoint" in body

    def _post_with_endpoint_fallback(
        self,
        endpoints: tuple[str, ...],
        payload: Dict[str, Any],
    ) -> Dict[str, Any]:
        if not endpoints:
            return {}

        for i, endpoint in enumerate(endpoints):
            try:
                resp = self._post(
                    endpoint,
                    json=payload,
                    headers=self._headers(),
                    allow_statuses=(404, 422),
                )
            except (AuthenticationError, RateLimitError, IntegrationError) as exc:
                raise ApolloError(str(exc)) from exc

            if resp.status_code == 404 and i < len(endpoints) - 1:
                continue
            if self._is_endpoint_deprecation_response(resp) and i < len(endpoints) - 1:
                continue
            if resp.status_code == 404:
                return {}
            if resp.status_code == 422:
                raise ApolloError(f"Apollo validation error 422: {resp.text[:240]}")

            try:
                return resp.json()
            except Exception as exc:
                raise ApolloError(f"Failed to parse Apollo response: {exc}") from exc

        return {}

    def enrich_company(self, domain: str, fields: Optional[list[str]] = None) -> Dict[str, Any]:
        domain_clean = (domain or "").strip()
        if not domain_clean:
            return {}

        try:
            resp = self._post(
                ENRICH_URL,
                json={"domain": domain_clean},
                headers=self._headers(),
                allow_statuses=(404,),
            )
        except (AuthenticationError, RateLimitError, IntegrationError) as exc:
            raise ApolloError(str(exc)) from exc

        if resp.status_code == 404:
            return {}

        try:
            data = resp.json()
        except Exception as exc:  # pragma: no cover - network parsing
            raise ApolloError(f"Failed to parse Apollo response: {exc}") from exc

        company = data.get("company") or data.get("data") or data
        result = _extract_fields(company if isinstance(company, dict) else {})
        if fields:
            return {k: result.get(k) for k in fields if k in result}
        return result

    def enrich_contact(
        self,
        email: Optional[str] = None,
        first_name: Optional[str] = None,
        last_name: Optional[str] = None,
        company: Optional[str] = None,
        domain: Optional[str] = None,
        linkedin_url: Optional[str] = None,
        fields: Optional[List[str]] = None,
    ) -> Dict[str, Any]:
        payload: Dict[str, Any] = {}
        if email:
            payload["email"] = email
        if first_name:
            payload["first_name"] = first_name
        if last_name:
            payload["last_name"] = last_name
        if company:
            payload["organization_name"] = company
        if domain:
            payload["domain"] = domain
        if linkedin_url:
            payload["linkedin_url"] = linkedin_url

        try:
            resp = self._post(
                PEOPLE_MATCH_URL,
                json=payload,
                headers=self._headers(),
                allow_statuses=(404,),
            )
        except (AuthenticationError, RateLimitError, IntegrationError) as exc:
            raise ApolloError(str(exc)) from exc

        if resp.status_code == 404:
            return {}

        try:
            data = resp.json()
        except Exception as exc:
            raise ApolloError(f"Failed to parse Apollo response: {exc}") from exc

        person = data.get("person") or data.get("data") or data
        normalized = _extract_contact_fields(person if isinstance(person, dict) else {})
        if fields:
            return {k: normalized.get(k) for k in fields if k in normalized}
        return normalized

    def search_people(self, request: Dict[str, Any]) -> Dict[str, Any]:
        if not isinstance(request, dict):
            raise ApolloError("Apollo people search request must be a JSON object.")
        payload = dict(request)
        payload.setdefault("page", 1)
        payload.setdefault("per_page", 25)
        return self._post_with_endpoint_fallback(PEOPLE_SEARCH_ENDPOINTS, payload)

    def search_companies(self, request: Dict[str, Any]) -> Dict[str, Any]:
        if not isinstance(request, dict):
            raise ApolloError("Apollo company search request must be a JSON object.")
        payload = dict(request)
        payload.setdefault("page", 1)
        payload.setdefault("per_page", 25)
        return self._post_with_endpoint_fallback(COMPANY_SEARCH_ENDPOINTS, payload)


def enrich_company(domain: str, fields: Optional[list[str]] = None) -> Dict[str, Any]:
    client = ApolloClient()
    try:
        return client.enrich_company(domain, fields)
    except ApolloError as exc:
        raise click.ClickException(str(exc)) from exc


def enrich_contact(
    email: Optional[str] = None,
    first_name: Optional[str] = None,
    last_name: Optional[str] = None,
    company: Optional[str] = None,
    domain: Optional[str] = None,
    linkedin_url: Optional[str] = None,
    fields: Optional[List[str]] = None,
) -> Dict[str, Any]:
    client = ApolloClient()
    try:
        return client.enrich_contact(
            email=email,
            first_name=first_name,
            last_name=last_name,
            company=company,
            domain=domain,
            linkedin_url=linkedin_url,
            fields=fields,
        )
    except ApolloError as exc:
        raise click.ClickException(str(exc)) from exc


def search_people(request: Dict[str, Any]) -> Dict[str, Any]:
    client = ApolloClient()
    try:
        return client.search_people(request)
    except ApolloError as exc:
        raise click.ClickException(str(exc)) from exc


def search_companies(request: Dict[str, Any]) -> Dict[str, Any]:
    client = ApolloClient()
    try:
        return client.search_companies(request)
    except ApolloError as exc:
        raise click.ClickException(str(exc)) from exc


__all__ = [
    "ApolloError",
    "ApolloClient",
    "get_apollo_api_key",
    "enrich_company",
    "enrich_contact",
    "search_people",
    "search_companies",
]
