"""
Tradeshow workflow endpoints for checking existing records in Salesforce
"""

import re
from typing import List, Dict, Any
from fastapi import APIRouter, Depends, HTTPException, Body, UploadFile, File, Header
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel

from ...auth import get_current_user
from ...auth_security import require_account
from ...db import get_session
from ...models import User
from ...services.salesforce_gateway import SalesforceGateway, get_salesforce_gateway

router = APIRouter(prefix="/api/v2", tags=["tradeshow"])


class CheckExistingRecordsRequest(BaseModel):
    emails: List[str]


def chunks(lst, n):
    """Yield successive n-sized chunks from list."""
    for i in range(0, len(lst), n):
        yield lst[i : i + n]


def is_valid_email(email: str) -> bool:
    """Basic email validation to prevent injection"""
    return bool(re.match(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", email))


def escape_soql(value: str) -> str:
    """Escape special characters for SOQL strings"""
    if not value:
        return ""
    # Escape backslashes first, then single quotes
    return value.replace("\\", "\\\\").replace("'", "\\'")


@router.post("/check-existing-records")
async def check_existing_records(
    payload: dict = Body(...),
    db: AsyncSession = Depends(get_session),
    current_user: User = Depends(get_current_user),
    sf: SalesforceGateway = Depends(get_salesforce_gateway),
) -> Dict[str, Any]:
    """
    Batch check if emails exist as Leads or Contacts in Salesforce.
    Returns a dictionary mapping email -> {type: 'Lead'|'Contact', id: sfId}
    """

    emails = payload.get("emails", [])

    # Filter and validate emails
    valid_emails = [
        e.lower().strip() for e in emails if e and is_valid_email(e.strip())
    ]

    if not valid_emails:
        return {}

    # SF gateway is injected via dependency - no need to create client

    try:
        existing = {}

        # Deduplicate (already validated and lowercased)
        unique_emails = list(set(valid_emails))

        # Process in batches of 200 (SOQL IN clause limit)
        for email_batch in chunks(unique_emails, 200):
            # Escape each email for SOQL
            escaped_emails = [escape_soql(email) for email in email_batch]
            email_list = "','".join(escaped_emails)

            # Check Leads
            try:
                lead_soql = (
                    f"SELECT Id, Email FROM Lead WHERE Email IN ('{email_list}')"
                )
                lead_results = await sf.soql(lead_soql)

                for lead in lead_results.get("records", []):
                    if lead.get("Email"):
                        email_key = lead["Email"].lower()
                        existing[email_key] = {"type": "Lead", "id": lead["Id"]}
            except Exception as e:
                print(f"Error querying Leads: {e}")
                # Continue to check Contacts even if Lead query fails

            # Check Contacts
            try:
                contact_soql = (
                    f"SELECT Id, Email FROM Contact WHERE Email IN ('{email_list}')"
                )
                contact_results = await sf.soql(contact_soql)

                for contact in contact_results.get("records", []):
                    if contact.get("Email"):
                        email_key = contact["Email"].lower()
                        # Contact takes precedence over Lead if both exist
                        existing[email_key] = {"type": "Contact", "id": contact["Id"]}
            except Exception as e:
                print(f"Error querying Contacts: {e}")

        return existing

    except Exception as e:
        # Log error but don't fail - frontend handles missing duplicate detection
        print(f"Error checking existing records: {e}")
        return {}


# ===== Tradeshow Import MVP =====


class ImportResponse(BaseModel):
    import_id: str
    sample_rows: int
    config_version: str
    artifact_url: str  # signed URL (24h)
    message: str = "import accepted"


class CommitRequest(BaseModel):
    import_id: str


class ReportResponse(BaseModel):
    job_id: str
    matched_count: int
    new_count: int
    undo_token: str
    summary: dict


@router.post("/tradeshow/import", response_model=ImportResponse)
async def import_tradeshow(
    file: UploadFile = File(...),
    idempotency_key: str | None = Header(default=None),
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
):
    """
    - Reject > 50 MB (configurable via env TRADESHOW_MAX_UPLOAD_MB, default 50)
    - Normalize encoding to UTF-8
    - Validate header count > 0
    - Sample ≤5000 rows -> call Intelligence.auto_configure(sample)
    - Persist original artifact and sample as signed URLs (24h)
    - Persist a new import record keyed by content hash + idempotency_key
    - Return import_id, sample_rows, config_version, artifact_url
    """
    from ...services.tradeshow_service import TradeshowService

    svc = TradeshowService(db)
    return await svc.accept_import(file, idempotency_key, account_id)


@router.post("/tradeshow/commit")
async def commit_tradeshow(
    body: CommitRequest,
    db: AsyncSession = Depends(get_session),
    account_id: str = Depends(require_account),
):
    """
    - Look up saved config from `import` step
    - Kick jobs/run with full dataset using that config
    - Return 202 + job reference
    """
    from ...services.tradeshow_service import TradeshowService

    svc = TradeshowService(db)
    return await svc.commit(body.import_id, account_id)


@router.get("/tradeshow/{job_id}/report", response_model=ReportResponse)
async def get_report(
    job_id: str,
    include: str | None = None,
    account_id: str = Depends(require_account),
    db: AsyncSession = Depends(get_session),
):
    """
    - Return matched/new splits, undo_token, and summary (explainability aggregates)
    """
    from ...services.tradeshow_service import TradeshowService

    svc = TradeshowService(db)
    rep = await svc.report(job_id)
    if not rep or str(rep.get("account_id")) != str(account_id):
        raise HTTPException(status_code=404, detail="report not found")
    return rep
