"""
Field mapping configuration endpoints for enrichment.

Allows per-account mapping of logical enrichment fields (email, phone, linkedin_url)
to actual Salesforce field API names.
"""

import logging
from typing import Literal

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.exc import ProgrammingError

from ...auth_core import get_current_account
from ...db import get_session
from ...models import EnrichmentFieldMapping

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/v2/enrichment/config", tags=["enrichment-config"])


class SaveMappingsRequest(BaseModel):
    """Request to save field mappings."""

    account_mappings: dict = {}
    contact_mappings: dict = {}
    lead_mappings: dict = {}


class MappingsResponse(BaseModel):
    """Response with current mappings."""

    account_mappings: dict
    contact_mappings: dict
    lead_mappings: dict


class FieldInfo(BaseModel):
    """Salesforce field metadata."""

    name: str
    label: str
    type: str


class AvailableFieldsResponse(BaseModel):
    """Available Salesforce fields."""

    fields: list[FieldInfo]


@router.get("/field-mappings", response_model=MappingsResponse)
async def get_field_mappings(
    account_id: str = Depends(get_current_account),
    db: AsyncSession = Depends(get_session),
):
    """
    Get current field mappings for account.

    Returns empty dicts if no custom mappings configured (uses standard fields).
    """
    try:
        result = await db.execute(
            select(EnrichmentFieldMapping).where(
                EnrichmentFieldMapping.account_id == account_id
            )
        )
        row = result.scalar_one_or_none()
    except ProgrammingError as exc:
        message = str(exc).lower()
        if "account_mappings" in message:
            logger.error(
                "Account mappings column missing for account %s. "
                "Apply migration 20251017_add_account_mappings.",
                account_id,
            )
            raise HTTPException(
                status_code=503,
                detail=(
                    "Account-level field mappings are not available yet. "
                    "Run `alembic upgrade 20251017_add_account_mappings` (or `upgrade head`) "
                    "to add the account_mappings column."
                ),
            ) from exc
        raise

    if not row:
        logger.debug(f"No custom mappings for account {account_id}, using defaults")
        return MappingsResponse(
            account_mappings={}, contact_mappings={}, lead_mappings={}
        )

    return MappingsResponse(
        account_mappings=getattr(row, "account_mappings", {}) or {},
        contact_mappings=row.contact_mappings,
        lead_mappings=row.lead_mappings,
    )


@router.post("/field-mappings", response_model=MappingsResponse)
async def save_field_mappings(
    request: SaveMappingsRequest,
    account_id: str = Depends(get_current_account),
    db: AsyncSession = Depends(get_session),
):
    """
    Save custom field mappings for enrichment.

    Example:
    ```json
    {
      "contact_mappings": {
        "linkedin_url": "LinkedIn_URL__c",
        "mobile_phone": "Mobile__c"
      },
      "lead_mappings": {
        "linkedin_url": "LinkedIn__c"
      }
    }
    ```
    """
    logger.info(f"Saving field mappings for account {account_id}")

    try:
        result = await db.execute(
            select(EnrichmentFieldMapping).where(
                EnrichmentFieldMapping.account_id == account_id
            )
        )
        row = result.scalar_one_or_none()
    except ProgrammingError as exc:
        message = str(exc).lower()
        if "account_mappings" in message:
            logger.error(
                "Account mappings column missing for account %s during save. "
                "Apply migration 20251017_add_account_mappings.",
                account_id,
            )
            raise HTTPException(
                status_code=503,
                detail=(
                    "Account-level field mappings cannot be saved until the "
                    "`account_mappings` column exists. "
                    "Run `alembic upgrade 20251017_add_account_mappings`."
                ),
            ) from exc
        raise

    if not row:
        # Create new
        row = EnrichmentFieldMapping(
            account_id=account_id,
            account_mappings=request.account_mappings,
            contact_mappings=request.contact_mappings,
            lead_mappings=request.lead_mappings,
        )
        db.add(row)
        logger.info(f"Created new field mappings for account {account_id}")
    else:
        # Update existing
        if hasattr(row, "account_mappings"):
            row.account_mappings = request.account_mappings
        row.contact_mappings = request.contact_mappings
        row.lead_mappings = request.lead_mappings
        logger.info(f"Updated field mappings for account {account_id}")

    await db.commit()
    await db.refresh(row)

    return MappingsResponse(
        account_mappings=getattr(row, "account_mappings", {}) or {},
        contact_mappings=row.contact_mappings,
        lead_mappings=row.lead_mappings,
    )


@router.get("/available-fields", response_model=AvailableFieldsResponse)
async def get_available_fields(
    object_type: Literal["Contact", "Lead", "Account"],
    account_id: str = Depends(get_current_account),
):
    """
    Get all updateable Salesforce fields for field mapping UI.

    Returns fields that can be written to, filtered by FLS.
    Excludes complex types like address and location.
    """
    logger.debug(f"Fetching available {object_type} fields for account {account_id}")

    try:
        # Get gateway for this account
        from ...services.salesforce_gateway import SalesforceGateway
        from ...db import AsyncSessionLocal

        async with AsyncSessionLocal() as session:
            gateway = await SalesforceGateway.for_tenant(account_id, session)
            describe = await gateway.describe(object_type)

        # Filter to updateable fields only
        fields = [
            FieldInfo(
                name=f["name"],
                label=f["label"],
                type=f["type"],
            )
            for f in describe.get("fields", [])
            if f.get("updateable", False)
            and f.get("type") not in ["address", "location"]
        ]

        # Sort by label for better UX
        fields.sort(key=lambda x: x.label.lower())

        logger.debug(f"Found {len(fields)} updateable fields for {object_type}")
        return AvailableFieldsResponse(fields=fields)

    except Exception as e:
        logger.error(f"Failed to fetch Salesforce fields for {object_type}: {e}")
        raise HTTPException(
            status_code=500, detail=f"Failed to fetch Salesforce fields: {str(e)}"
        )
