"""People Data Labs API endpoints for Google Sheets integration"""

import hashlib
import json
from typing import Any, Dict, List, Optional
from fastapi import APIRouter, HTTPException, Depends, Request
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession

from fmatch.saas.integrations.people_data_labs import (
    PeopleDataLabsAdapter,
    PDLCompanyEnrichmentRequest,
    PDLPersonEnrichmentRequest,
)
from fmatch.saas.auth import verify_api_key
from fmatch.saas.db import get_session
from fmatch.saas.config.enrichment import ENRICHMENT_ENABLED
from fmatch.saas.services.enrichment_usage_tracker import EnrichmentUsageTracker


def require_enrichment_enabled():
    if not ENRICHMENT_ENABLED:
        raise HTTPException(
            status_code=404,
            detail="Enrichment features are not currently available",
        )


router = APIRouter(
    prefix="/api/v1/pdl",
    tags=["pdl"],
    dependencies=[Depends(require_enrichment_enabled)],
)

ENRICH_FIELD_PREFIX = "enrich_"


class PDLEnrichRequest(BaseModel):
    """Request for PDL enrichment from Google Sheets"""

    source: Dict[str, Any] = Field(..., description="Source data with headers and rows")
    requests: List[Dict[str, Any]] = Field(..., description="PDL enrichment requests")
    provider: str = Field(default="pdl")
    type: str = Field(..., description="Enrichment type: company or person")
    output_fields: List[str] = Field(
        default_factory=list, description="Fields to include in output"
    )


class PDLSearchRequest(BaseModel):
    """Request for PDL search"""

    type: str = Field(..., description="Search type: company_search or person_search")
    sql_query: Optional[str] = Field(None, description="SQL query for search")
    es_query: Optional[Dict] = Field(None, description="Elasticsearch query for search")
    max_results: int = Field(default=100, ge=1, le=1000)
    output_fields: List[str] = Field(default_factory=list)
    provider: str = Field(default="pdl")


class PDLEnrichResponse(BaseModel):
    """Response for PDL enrichment"""

    ok: bool = True
    headers: List[str]
    rows: List[List[Any]]
    matched: int = 0
    credits: float = 0
    errors: List[str] = Field(default_factory=list)


class PDLBalanceResponse(BaseModel):
    """Balance snapshot for enrichment credits"""

    account_id: str
    credits_available: int = 0
    allowance_remaining: int = 0
    pack_remaining: int = 0
    daily_remaining: Optional[int] = None
    monthly_remaining: Optional[int] = None
    credits_per_request: float = 0.0
    price_per_request_usd: float = 0.0
    day_start_utc: Optional[str] = None
    next_day_start_utc: Optional[str] = None
    is_free_tier: bool = False
    raw: Dict[str, Any] = Field(default_factory=dict)


class PDLEstimateResponse(BaseModel):
    """Estimate for enrichment credit usage"""

    estimated_requests: int = 0


class PDLJobsRequest(BaseModel):
    """Request to fetch job postings"""

    company_name: Optional[str] = None
    company_domain: Optional[str] = None
    job_title: Optional[str] = None
    location: Optional[str] = None
    size: int = Field(default=10, ge=1, le=100)


class PDLTableResponse(BaseModel):
    ok: bool = True
    headers: List[str]
    rows: List[List[Any]]
    matched: int = 0
    credits: float = 0
    errors: List[str] = Field(default_factory=list)


class PDLTechExtractionRequest(BaseModel):
    """Company technographics extraction (batch)"""

    requests: List[Dict[str, Any]]
    min_likelihood: int = Field(default=0, ge=0, le=10)


class PDLEmploymentExtractionRequest(BaseModel):
    """Employment linkage extraction (batch)"""

    requests: List[Dict[str, Any]]
    min_likelihood: int = Field(default=0, ge=0, le=10)


@router.post("/enrich", response_model=PDLEnrichResponse)
async def enrich_data(
    request: PDLEnrichRequest,
    raw_request: Request,
    api_key: str = Depends(verify_api_key),
    db: AsyncSession = Depends(get_session),
) -> PDLEnrichResponse:
    """Batch enrichment endpoint for Google Sheets with usage tracking"""

    adapter = PeopleDataLabsAdapter()

    if not adapter.available():
        raise HTTPException(status_code=503, detail="PDL API key not configured")

    # Initialize usage tracker
    tracker = EnrichmentUsageTracker(db)
    account_id, user_email = tracker.resolve_account(raw_request)

    # Generate idempotency key and request hash for tracking
    payload = json.dumps(
        {
            "type": request.type,
            "requests": request.requests[:100],
            "output_fields": sorted(request.output_fields),
        },
        sort_keys=True,
    )
    payload_hash = hashlib.sha256(payload.encode()).hexdigest()
    idem_key = f"pdl-sheets-{request.type}-{payload_hash[:16]}"

    source_headers = request.source.get("headers", [])
    source_rows = request.source.get("rows", [])

    if not source_rows:
        return PDLEnrichResponse(
            ok=True, headers=source_headers, rows=[], matched=0, credits=0
        )

    # Estimate and check quota
    estimated_requests = min(len(request.requests), 100)
    preflight = await tracker.preflight(
        account_id=account_id,
        estimated_requests=estimated_requests,
        idempotency_key=idem_key,
        request_hash=payload_hash,
        reserve=True,
        job_id=f"sheets-enrich-{request.type}",
    )

    # Handle idempotent replay
    if preflight.get("replay"):
        raise HTTPException(
            status_code=409,
            detail="This request was already processed. Check your previous results.",
        )

    # Handle quota denial
    if not preflight.get("allowed"):
        raise HTTPException(
            status_code=preflight.get("status_code", 429),
            detail={
                "error": "quota_exceeded",
                "message": preflight.get("reason", "Insufficient credits"),
                "allowance_remaining": preflight.get("allowance_remaining", 0),
                "pack_remaining": preflight.get("pack_remaining", 0),
            },
        )

    try:
        pdl_requests = _build_pdl_requests(request)
        if request.type == "company":
            result = adapter.bulk_company_enrichment(pdl_requests)
        else:
            result = adapter.bulk_person_enrichment(pdl_requests)

        actual_requests = len(pdl_requests)

        if "error" in result:
            # Release reservation on error
            await tracker.release(account_id, idem_key)
            raise HTTPException(status_code=500, detail=result["error"])

        # Process responses and build output rows
        responses = result.get("responses", [])
        enriched_headers = list(source_headers)
        enriched_rows = []
        matched = 0

        # Determine which enrichment fields to include
        enrich_fields_to_add = set()
        for field in request.output_fields:
            column_name = f"{ENRICH_FIELD_PREFIX}{field}"
            if column_name not in enriched_headers:
                enriched_headers.append(column_name)
                enrich_fields_to_add.add(field)

        # Process each row
        for i, source_row in enumerate(source_rows):
            enriched_row = list(source_row)

            # Add enrichment data if available
            if i < len(responses) and responses[i]:
                resp = responses[i]
                if resp.get("status") == 200 and "data" in resp:
                    matched += 1
                    data = resp["data"]

                    # Add requested fields
                    for field in enrich_fields_to_add:
                        value = _extract_field_value(data, field)
                        enriched_row.append(value)
                else:
                    # No match - add empty values
                    enriched_row.extend([""] * len(enrich_fields_to_add))
            else:
                # No response - add empty values
                enriched_row.extend([""] * len(enrich_fields_to_add))

            enriched_rows.append(enriched_row)

        # Finalize usage tracking
        await tracker.finalize(
            account_id=account_id,
            user_email=user_email,
            idempotency_key=idem_key,
            request_hash=payload_hash,
            actual_requests=actual_requests,  # Provider charges per request, not match
            matches_found=matched,
            operation="enrich",
            enrichment_type=request.type,
            provider="pdl",
            job_id=f"sheets-enrich-{request.type}",
            chunk_id=None,
            meta={
                "records_scanned": len(source_rows),
                "candidates_found": len(request.requests),
                "source": "google_sheets",
                "output_fields": request.output_fields,
            },
        )

        return PDLEnrichResponse(
            ok=True,
            headers=enriched_headers,
            rows=enriched_rows,
            matched=matched,
            credits=actual_requests,  # Accurate credit count
        )

    except HTTPException:
        raise
    except Exception as e:
        # Try to release reservation on unexpected error
        try:
            await tracker.release(account_id, idem_key)
        except Exception:
            pass
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/balances", response_model=PDLBalanceResponse)
async def get_pdl_balances(
    raw_request: Request,
    api_key: str = Depends(verify_api_key),
    db: AsyncSession = Depends(get_session),
) -> PDLBalanceResponse:
    """Expose current enrichment credit balances for the requesting account."""

    tracker = EnrichmentUsageTracker(db)
    account_id, _ = tracker.resolve_account(raw_request)

    balances = await tracker.balances(account_id)

    allowance_remaining = int(balances.get("allowance_remaining") or 0)
    pack_remaining = int(balances.get("pack_remaining") or 0)
    credits_available = allowance_remaining + pack_remaining
    price_per_request = float(balances.get("price_per_request_usd") or 0.0)

    def _optional_int(value: Any) -> Optional[int]:
        if value is None:
            return None
        try:
            return int(value)
        except (TypeError, ValueError):
            return None

    # Treat zero-priced tiers or empty balances as free tier for UX messaging.
    is_free_tier = price_per_request <= 0 or credits_available <= 0

    return PDLBalanceResponse(
        account_id=str(account_id),
        credits_available=credits_available,
        allowance_remaining=allowance_remaining,
        pack_remaining=pack_remaining,
        daily_remaining=_optional_int(balances.get("daily_remaining")),
        monthly_remaining=_optional_int(balances.get("monthly_remaining")),
        credits_per_request=float(balances.get("credits_per_request") or 0.0),
        price_per_request_usd=price_per_request,
        day_start_utc=balances.get("day_start_utc"),
        next_day_start_utc=balances.get("next_day_start_utc"),
        is_free_tier=is_free_tier,
        raw=balances,
    )


@router.post("/estimate", response_model=PDLEstimateResponse)
async def estimate_enrichment(
    request: PDLEnrichRequest, api_key: str = Depends(verify_api_key)
) -> PDLEstimateResponse:
    """Return the number of provider requests required for this enrichment payload."""

    adapter = PeopleDataLabsAdapter()
    if not adapter.available():
        raise HTTPException(status_code=503, detail="PDL API key not configured")

    pdl_requests = _build_pdl_requests(request)
    return PDLEstimateResponse(estimated_requests=len(pdl_requests))


@router.post("/search", response_model=PDLEnrichResponse)
async def search_data(
    request: PDLSearchRequest, api_key: str = Depends(verify_api_key)
) -> PDLEnrichResponse:
    """Search endpoint for Google Sheets"""

    adapter = PeopleDataLabsAdapter()

    if not adapter.available():
        raise HTTPException(status_code=503, detail="PDL API key not configured")

    try:
        # Perform search based on type
        if request.type == "company_search":
            result = adapter.search_companies(
                sql_query=request.sql_query,
                es_query=request.es_query,
                size=request.max_results,
            )
        else:  # person_search
            result = adapter.search_people(
                sql_query=request.sql_query,
                es_query=request.es_query,
                size=request.max_results,
            )

        if "error" in result:
            raise HTTPException(status_code=500, detail=result["error"])

        # Extract data from search results
        data_list = result.get("data", [])

        if not data_list:
            return PDLEnrichResponse(ok=True, headers=[], rows=[], matched=0, credits=0)

        # Build headers based on requested fields or all available fields
        if request.output_fields:
            headers = request.output_fields
        else:
            # Use all fields from first result
            headers = list(data_list[0].keys()) if data_list else []

        # Build rows
        rows = []
        for item in data_list:
            row = []
            for field in headers:
                value = _extract_field_value(item, field)
                row.append(value)
            rows.append(row)

        return PDLEnrichResponse(
            ok=True,
            headers=headers,
            rows=rows,
            matched=len(rows),
            credits=len(rows),  # Search API charges per result
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


def _build_pdl_requests(payload: PDLEnrichRequest) -> List[Any]:
    """Convert incoming payload into provider request objects."""

    trimmed_requests = payload.requests[:100]
    requests: List[Any] = []

    if payload.type == "company":
        for req_dict in trimmed_requests:
            requests.append(
                PDLCompanyEnrichmentRequest(
                    name=req_dict.get("name"),
                    website=req_dict.get("website"),
                    domain=req_dict.get("domain"),
                    ticker=req_dict.get("ticker"),
                    linkedin_url=req_dict.get("linkedin_url"),
                    location=req_dict.get("location"),
                    min_likelihood=req_dict.get("min_likelihood", 0),
                )
            )
    elif payload.type == "person":
        for req_dict in trimmed_requests:
            requests.append(
                PDLPersonEnrichmentRequest(
                    email=req_dict.get("email"),
                    first_name=req_dict.get("first_name"),
                    last_name=req_dict.get("last_name"),
                    company=req_dict.get("company"),
                    phone=req_dict.get("phone"),
                    profile=req_dict.get("profile") or req_dict.get("linkedin_url"),
                    min_likelihood=req_dict.get("min_likelihood", 0),
                )
            )
    else:
        raise HTTPException(
            status_code=400,
            detail=f"Unsupported enrichment type '{payload.type}'. Use 'company' or 'person'.",
        )

    return requests


def _extract_field_value(data: Dict, field: str) -> Any:
    """Extract a field value from PDL data, handling nested structures"""

    # Handle nested field access (e.g., "job.title")
    if "." in field:
        parts = field.split(".")
        value = data
        for part in parts:
            if isinstance(value, dict):
                value = value.get(part)
            else:
                return ""
        return _format_value(value)

    # Handle special fields
    if field == "work_email":
        # Try to get work email from various sources
        if "work_email" in data:
            return data["work_email"]
        elif "emails" in data and isinstance(data["emails"], list) and data["emails"]:
            # Return first email marked as current/primary
            for email in data["emails"]:
                if isinstance(email, dict) and email.get("current"):
                    return email.get("address", "")
            # Fallback to first email
            return (
                data["emails"][0].get("address", "")
                if isinstance(data["emails"][0], dict)
                else data["emails"][0]
            )
        return ""

    elif field == "phone_numbers":
        if "phone_numbers" in data and isinstance(data["phone_numbers"], list):
            # Return comma-separated list of phone numbers
            phones = []
            for phone in data["phone_numbers"]:
                if isinstance(phone, dict):
                    phones.append(phone.get("number", ""))
                else:
                    phones.append(str(phone))
            return ", ".join(filter(None, phones))
        return ""

    elif field == "personal_emails":
        # Gather non-work emails if available
        emails = data.get("emails")
        out = []
        if isinstance(emails, list):
            for e in emails:
                if isinstance(e, dict):
                    addr = e.get("address") or e.get("email")
                    typ = (e.get("type") or "").lower()
                    if addr and (typ in ("personal", "other") or not typ):
                        out.append(addr)
                elif isinstance(e, str):
                    out.append(e)
        return ", ".join(dict.fromkeys(out)) if out else ""

    elif field == "linkedin_url":
        # Try various common places
        if data.get("linkedin_url"):
            return data.get("linkedin_url")
        profiles = data.get("profiles") or data.get("social_profiles")
        if isinstance(profiles, list):
            for p in profiles:
                if isinstance(p, dict) and (
                    p.get("site") == "linkedin"
                    or "linkedin.com" in str(p.get("url") or "")
                ):
                    return p.get("url", "")
        return ""

    elif field == "technologies":
        if "technologies" in data and isinstance(data["technologies"], list):
            # Return comma-separated list of technologies
            return ", ".join(data["technologies"])
        return ""

    elif field == "job_title":
        # Try to get current job title
        if "job_title" in data:
            return data["job_title"]
        elif (
            "experience" in data
            and isinstance(data["experience"], list)
            and data["experience"]
        ):
            # Get title from most recent/current experience
            for exp in data["experience"]:
                if isinstance(exp, dict) and exp.get("current"):
                    return exp.get("title", "")
            # Fallback to first experience
            return (
                data["experience"][0].get("title", "")
                if isinstance(data["experience"][0], dict)
                else ""
            )
        return ""

    elif field == "job_company_name":
        # Try to get current company name
        if "job_company_name" in data:
            return data["job_company_name"]
        elif (
            "experience" in data
            and isinstance(data["experience"], list)
            and data["experience"]
        ):
            for exp in data["experience"]:
                if isinstance(exp, dict) and exp.get("current"):
                    company = exp.get("company", {})
                    if isinstance(company, dict):
                        return company.get("name", "")
                    return str(company)
            # Fallback to first experience
            if isinstance(data["experience"][0], dict):
                company = data["experience"][0].get("company", {})
                if isinstance(company, dict):
                    return company.get("name", "")
        return ""

    elif field == "job_company_website":
        # Try to get current company website
        if "job_company_website" in data:
            return data["job_company_website"]
        exp = data.get("experience")
        if isinstance(exp, list) and exp:
            for item in exp:
                if isinstance(item, dict) and item.get("current"):
                    company = item.get("company")
                    if isinstance(company, dict):
                        return company.get("website", "")
        return ""

    # Default field extraction
    value = data.get(field, "")
    return _format_value(value)


def _format_value(value: Any) -> str:
    """Format a value for spreadsheet output"""

    if value is None:
        return ""
    elif isinstance(value, bool):
        return "Yes" if value else "No"
    elif isinstance(value, (list, dict)):
        # Convert complex types to string representation
        if isinstance(value, list) and value and isinstance(value[0], str):
            # Simple string list - join with commas
            return ", ".join(value)
        # For complex structures, use string representation
        return str(value)
    else:
        return str(value)


@router.post("/jobs", response_model=PDLTableResponse)
async def get_job_postings(
    request: PDLJobsRequest,
    api_key: str = Depends(verify_api_key),
) -> PDLTableResponse:
    """Return recent job postings as a table for account intelligence."""
    adapter = PeopleDataLabsAdapter()
    if not adapter.available():
        raise HTTPException(status_code=503, detail="PDL API key not configured")

    result = adapter.get_job_postings(
        company_name=request.company_name,
        company_domain=request.company_domain,
        job_title=request.job_title,
        location=request.location,
        size=request.size,
    )
    if "error" in result:
        raise HTTPException(status_code=500, detail=result["error"])
    data_list = result.get("data", [])
    if not data_list:
        return PDLTableResponse(ok=True, headers=[], rows=[], matched=0, credits=0)

    # Choose common job fields
    headers = [
        "job_title",
        "company_name",
        "company_domain",
        "location",
        "posted_at",
        "url",
    ]
    rows: List[List[Any]] = []
    for item in data_list:
        row = [
            _extract_field_value(item, "job_title"),
            _extract_field_value(item, "company_name"),
            _extract_field_value(item, "company_domain"),
            _extract_field_value(item, "location"),
            _extract_field_value(item, "posted_at"),
            _extract_field_value(item, "url"),
        ]
        rows.append(row)

    return PDLTableResponse(
        ok=True, headers=headers, rows=rows, matched=len(rows), credits=float(len(rows))
    )


@router.post("/tech", response_model=PDLTableResponse)
async def extract_technographics(
    request: PDLTechExtractionRequest,
    api_key: str = Depends(verify_api_key),
) -> PDLTableResponse:
    """Extract company technologies for a batch of company queries."""
    adapter = PeopleDataLabsAdapter()
    if not adapter.available():
        raise HTTPException(status_code=503, detail="PDL API key not configured")

    # Build PDLCompanyEnrichmentRequest list
    pdl_requests: List[PDLCompanyEnrichmentRequest] = []
    for req in request.requests[:100]:
        pdl_requests.append(
            PDLCompanyEnrichmentRequest(
                name=req.get("name"),
                website=req.get("website"),
                domain=req.get("domain"),
                linkedin_url=req.get("linkedin_url"),
                min_likelihood=req.get("min_likelihood", request.min_likelihood),
            )
        )

    result = adapter.bulk_company_enrichment(pdl_requests)
    if "error" in result:
        raise HTTPException(status_code=500, detail=result["error"])

    responses = result.get("responses", [])
    headers = ["name", "domain", "technologies"]
    rows: List[List[Any]] = []

    for resp in responses:
        if resp and resp.get("status") in (200, None) and "data" in resp:
            data = resp["data"]
            name = _extract_field_value(data, "name")
            domain = _extract_field_value(data, "domain") or _extract_field_value(
                data, "website"
            )
            techs = _extract_field_value(data, "technologies")
            rows.append([name, domain, techs])
        else:
            rows.append(["", "", ""])  # preserve alignment

    return PDLTableResponse(
        ok=True, headers=headers, rows=rows, matched=len([r for r in rows if any(r)])
    )


@router.post("/employment", response_model=PDLTableResponse)
async def extract_employment(
    request: PDLEmploymentExtractionRequest,
    api_key: str = Depends(verify_api_key),
) -> PDLTableResponse:
    """Extract current (and basic past) employment info for people.

    Returns a table with: full_name, job_title, job_company_name, job_company_website,
    job_start_date, job_end_date, job_is_current
    """
    adapter = PeopleDataLabsAdapter()
    if not adapter.available():
        raise HTTPException(status_code=503, detail="PDL API key not configured")

    # Build person enrichment requests
    pdl_requests: List[PDLPersonEnrichmentRequest] = []
    for req in request.requests[:100]:
        pdl_requests.append(
            PDLPersonEnrichmentRequest(
                email=req.get("email"),
                first_name=req.get("first_name"),
                last_name=req.get("last_name"),
                company=req.get("company"),
                profile=req.get("profile") or req.get("linkedin_url"),
                min_likelihood=req.get("min_likelihood", request.min_likelihood),
            )
        )

    result = adapter.bulk_person_enrichment(pdl_requests)
    if "error" in result:
        raise HTTPException(status_code=500, detail=result["error"])

    responses = result.get("responses", [])
    headers = [
        "full_name",
        "job_title",
        "job_company_name",
        "job_company_website",
        "job_start_date",
        "job_end_date",
        "job_is_current",
    ]
    rows: List[List[Any]] = []

    for resp in responses:
        if resp and resp.get("status") in (200, None) and "data" in resp:
            data = resp["data"]
            # full_name with fallback
            full_name = data.get("full_name") or (
                (
                    (data.get("first_name") or "") + " " + (data.get("last_name") or "")
                ).strip()
            )
            # current employment from experience
            title = _extract_field_value(data, "job_title")
            company = _extract_field_value(data, "job_company_name")
            website = _extract_field_value(data, "job_company_website")
            start = ""
            end = ""
            is_current = ""
            exp = data.get("experience")
            if isinstance(exp, list) and exp:
                cur = None
                for e in exp:
                    if isinstance(e, dict) and e.get("current"):
                        cur = e
                        break
                if not cur and isinstance(exp[0], dict):
                    cur = exp[0]
                if isinstance(cur, dict):
                    start = cur.get("start_date") or cur.get("start") or ""
                    end = cur.get("end_date") or cur.get("end") or ""
                    is_current = bool(cur.get("current"))
            rows.append([full_name, title, company, website, start, end, is_current])
        else:
            rows.append(["", "", "", "", "", "", ""])  # preserve alignment

    return PDLTableResponse(
        ok=True, headers=headers, rows=rows, matched=len([r for r in rows if any(r)])
    )
