"""
LeadSquared activity push helpers shared by call_processor and orchestrate_pipeline.
"""

from __future__ import annotations

import json
import logging
from typing import Any, Dict, Optional

from db_handler import DatabaseHandler
from leadsquared_service import LeadSquaredService

logger = logging.getLogger(__name__)


def pick_customer_phone(call: Dict[str, Any]) -> str:
    direction = (call.get("direction") or "inbound").lower()
    customer_phone = str(call.get("customer_phone") or call.get("customer_callinfo") or "").strip()
    if customer_phone:
        return customer_phone
    callto = str(call.get("callto") or "").strip()
    callfrom = str(call.get("callfrom") or "").strip()
    clicktocalldid = str(call.get("clicktocalldid") or "").strip()
    if direction == "outbound" and callto:
        return callto
    if direction == "inbound" and callfrom:
        return callfrom
    return clicktocalldid or callto or callfrom


def call_dict_for_lsq_push(raw: Dict[str, Any]) -> Dict[str, Any]:
    """Shape a raw_calls row (from get_raw_call_details) for LSQ push."""
    return {
        "callid": raw.get("callid"),
        "direction": raw.get("direction") or "inbound",
        "agentname": raw.get("agentname") or "",
        "callfrom": raw.get("agent_callinfo"),
        "callto": raw.get("customer_callinfo"),
        "clicktocalldid": raw.get("customer_callinfo"),
        "customer_phone": raw.get("customer_callinfo"),
        "agent_callinfo": raw.get("agent_callinfo"),
        "customer_callinfo": raw.get("customer_callinfo"),
        "call_starttime": raw.get("call_starttime"),
    }


def _extract_nested_value(payload: Dict[str, Any], path: str) -> Any:
    if not isinstance(payload, dict) or not path:
        return None
    current = payload
    for part in path.split("."):
        if not isinstance(current, dict):
            return None
        current = current.get(part)
    return current


def _extract_lsq_activity_value(analysis: Dict[str, Any], call: Dict[str, Any], source_key: str) -> Any:
    def _raw_response_payload() -> Dict[str, Any]:
        raw_response = analysis.get("raw_response") if isinstance(analysis, dict) else None
        if isinstance(raw_response, str):
            try:
                raw_response = json.loads(raw_response)
            except json.JSONDecodeError:
                raw_response = None
        return raw_response if isinstance(raw_response, dict) else {}

    def _profile_value(profile: Dict[str, Any]) -> Any:
        profile_value = profile.get(source_key)
        if isinstance(profile_value, dict):
            return profile_value.get("value") or profile_value.get("summary") or profile_value.get("evidence")
        return profile_value

    if source_key == "sales_intent":
        value = _extract_nested_value(analysis, "sentiment")
        if value is not None:
            return value

    value = _extract_nested_value(analysis, source_key)
    if value is not None:
        return value

    customer_profile = analysis.get("customer_profile")
    if isinstance(customer_profile, dict) and source_key in {"need", "budget", "timeline", "authority"}:
        value = _profile_value(customer_profile)
        if value is not None:
            return value

    raw_response = _raw_response_payload()
    if source_key == "customer_profile_summary":
        value = _extract_nested_value(raw_response, "customer_profile_summary")
        if value is not None:
            return value
    if source_key in {"need", "budget", "timeline", "authority"}:
        raw_profile = raw_response.get("customer_profile")
        if isinstance(raw_profile, dict):
            value = _profile_value(raw_profile)
            if value is not None:
                return value

    aliases = {
        "summary": ("summary", "overall_summary", "rich_summary"),
        "customer_profile_summary": ("customer_profile_summary",),
        "sales_intent": ("sentiment", "sales_intent", "intent", "call_purpose"),
        "quality_score": ("quality_score",),
    }
    for alias in aliases.get(source_key, ()):
        value = _extract_nested_value(analysis, alias)
        if value is not None:
            return value

    return _extract_nested_value(call, source_key)


def _first_present_str(*values: Any) -> Optional[str]:
    for value in values:
        if value is None:
            continue
        text = str(value).strip()
        if text:
            return text
    return None


def _lsq_activity_field_value(value: Any) -> Any:
    if isinstance(value, (dict, list)):
        return json.dumps(value, ensure_ascii=False)
    return value


def _normalize_lsq_activity_target(target: str) -> str:
    compact = str(target or "").strip()
    lower = compact.lower()
    if lower.startswith("customfield") and lower.replace("customfield", "", 1).isdigit():
        return f"mx_Custom_{lower.replace('customfield', '', 1)}"
    if lower in {"eventdescription", "intent"}:
        return "ActivityNote"
    return compact


def _lead_id_from_lsq_record(record: Dict[str, Any]) -> Optional[str]:
    if not isinstance(record, dict):
        return None
    return _first_present_str(
        record.get("ProspectID"),
        record.get("LeadId"),
        record.get("Id"),
        record.get("prospect_id"),
        record.get("lead_id"),
    )


def build_lsq_activity_payload(
    activity_cfg: Dict[str, Any],
    analysis: Dict[str, Any],
    call: Dict[str, Any],
    prospect_id: str,
    integration_config: Optional[Dict[str, Any]] = None,
) -> Optional[Dict[str, Any]]:
    if not activity_cfg or not activity_cfg.get("enabled"):
        return None
    activity_code = _first_present_str(activity_cfg.get("activity_code"))
    if not activity_code:
        return None

    payload: Dict[str, Any] = {
        "RelatedProspectId": prospect_id,
        "ActivityEvent": int(activity_code) if str(activity_code).isdigit() else activity_code,
    }
    schema_name = _first_present_str(activity_cfg.get("schema_name"))
    if schema_name:
        payload["SchemaName"] = schema_name

    field_mappings = activity_cfg.get("field_mappings") or {}
    if isinstance(field_mappings, str):
        try:
            field_mappings = json.loads(field_mappings)
        except json.JSONDecodeError:
            field_mappings = {}

    fields = []
    top_level_targets = {
        "ActivityNote",
        "ActivityDateTime",
        "ActivityOwnerEmail",
        "ProcessFilesAsync",
        "IncludeLeadScore",
        "ValidateDropDownOptions",
    }
    if isinstance(field_mappings, dict):
        for source_key, target_key in field_mappings.items():
            target = _first_present_str(target_key)
            if not target:
                continue
            target = _normalize_lsq_activity_target(target)
            value = _extract_lsq_activity_value(analysis, call, source_key)
            if value is None:
                continue
            if target in top_level_targets:
                payload[target] = _lsq_activity_field_value(value)
            else:
                fields.append({"SchemaName": target, "Value": _lsq_activity_field_value(value)})

    if fields:
        payload["Fields"] = fields

    from lsq_activity_helpers import apply_lsq_agent_attribution

    return apply_lsq_agent_attribution(payload, call, integration_config)


def _cached_lsq_prospect_id(db: DatabaseHandler, bid: str, phones: list[str]) -> Optional[str]:
    for raw_phone in phones:
        phone = _first_present_str(raw_phone)
        if not phone:
            continue
        cached = db.get_cached_crm_lead_by_phone(bid, "leadsquared", phone)
        if not cached:
            continue
        payload = cached.get("lead_payload") or {}
        lead_id = _first_present_str(
            payload.get("ProspectID"),
            payload.get("LeadId"),
            payload.get("Id"),
            cached.get("external_lead_id"),
        )
        if lead_id:
            return lead_id
    return None


def resolve_lsq_prospect_id(
    service: LeadSquaredService,
    call: Dict[str, Any],
    analysis: Dict[str, Any],
    db: Optional[DatabaseHandler] = None,
    bid: Optional[str] = None,
) -> Optional[str]:
    phone_candidates = [
        call.get("customer_phone"),
        call.get("customer_callinfo"),
        pick_customer_phone(call),
        call.get("callto"),
        call.get("callfrom"),
        call.get("clicktocalldid"),
    ]
    phones = []
    seen = set()
    for raw_phone in phone_candidates:
        phone = _first_present_str(raw_phone)
        if not phone or phone in seen:
            continue
        seen.add(phone)
        phones.append(phone)

    if db is not None and bid is not None:
        cached_id = _cached_lsq_prospect_id(db, bid, phones)
        if cached_id:
            logger.info("[%s] Matched LeadSquared lead from cache by phone for %s", bid, call.get("callid"))
            return cached_id

    email = _first_present_str(analysis.get("customer_email")) or _first_present_str(call.get("customer_email"))
    for phone in phones:
        phone_result = service.search_lead_by_phone(phone)
        if phone_result.get("success"):
            data = phone_result.get("data")
            if isinstance(data, list) and data:
                lead_id = _lead_id_from_lsq_record(data[0])
                if lead_id:
                    return lead_id
            elif isinstance(data, dict):
                lead_id = _lead_id_from_lsq_record(data)
                if lead_id:
                    return lead_id

    if email:
        email_result = service.search_lead_by_email(email)
        if email_result.get("success"):
            data = email_result.get("data")
            if isinstance(data, list) and data:
                lead_id = _lead_id_from_lsq_record(data[0])
                if lead_id:
                    return lead_id
            elif isinstance(data, dict):
                lead_id = _lead_id_from_lsq_record(data)
                if lead_id:
                    return lead_id

    return None


def push_leadsquared_activities(
    bid: str, call: Dict[str, Any], analysis: Dict[str, Any], db: DatabaseHandler
) -> None:
    integration = db.get_crm_integration(bid, "leadsquared") or {}
    if not integration.get("is_active") or not integration.get("has_credentials"):
        return

    config = integration.get("config") or {}
    if not config.get("enabled"):
        return

    creds = db.get_crm_credentials(bid, "leadsquared") or {}
    if not creds.get("access_key") or not creds.get("secret_key"):
        return

    service = LeadSquaredService(
        access_key=creds["access_key"],
        secret_key=creds["secret_key"],
        api_host=creds.get("api_host"),
    )

    activities = config.get("activities") or {}
    if not isinstance(activities, dict):
        return

    lead_phone = _first_present_str(
        call.get("customer_phone"),
        call.get("customer_callinfo"),
        pick_customer_phone(call),
        call.get("callto"),
        call.get("callfrom"),
    )
    prospect_id = resolve_lsq_prospect_id(service, call, analysis, db=db, bid=bid)

    if not prospect_id:
        message = "No LeadSquared lead matched the call phone"
        logger.info(
            "[%s] Skipping LeadSquared push for %s because no LSQ lead matched the call phone",
            bid,
            call.get("callid"),
        )
        try:
            db.log_crm_push(
                bid=bid,
                provider="leadsquared",
                status="skipped",
                callid=call.get("callid"),
                lead_phone=lead_phone,
                message=message,
            )
        except Exception:
            logger.exception("[%s] Failed to write LeadSquared push log for %s", bid, call.get("callid"))
        return

    for activity_key, activity_cfg in activities.items():
        try:
            payload = build_lsq_activity_payload(
                activity_cfg, analysis, call, prospect_id, integration_config=config
            )
            if not payload:
                try:
                    db.log_crm_push(
                        bid=bid,
                        provider="leadsquared",
                        status="skipped",
                        callid=call.get("callid"),
                        lead_phone=lead_phone,
                        crm_lead_id=prospect_id,
                        activity_key=activity_key,
                        message="Activity is disabled or missing activity code",
                    )
                except Exception:
                    logger.exception("[%s] Failed to write LeadSquared push log for %s", bid, call.get("callid"))
                continue
            result = service.create_activity(payload)
            if result.get("success"):
                logger.info(
                    "[%s] LeadSquared activity pushed for %s activity=%s agent=%s",
                    bid,
                    call.get("callid"),
                    activity_key,
                    (call.get("agentname") or call.get("agent_name") or "").strip() or "?",
                )
                try:
                    db.log_crm_push(
                        bid=bid,
                        provider="leadsquared",
                        status="pushed",
                        callid=call.get("callid"),
                        lead_phone=lead_phone,
                        crm_lead_id=prospect_id,
                        activity_key=activity_key,
                        activity_event=payload.get("ActivityEvent"),
                        message="LeadSquared activity pushed successfully",
                        payload_preview=payload,
                        response_preview=result.get("data"),
                    )
                except Exception:
                    logger.exception("[%s] Failed to write LeadSquared push log for %s", bid, call.get("callid"))
            else:
                logger.warning(
                    "[%s] LeadSquared activity push failed for %s activity=%s: %s",
                    bid,
                    call.get("callid"),
                    activity_key,
                    result.get("message"),
                )
                try:
                    db.log_crm_push(
                        bid=bid,
                        provider="leadsquared",
                        status="failed",
                        callid=call.get("callid"),
                        lead_phone=lead_phone,
                        crm_lead_id=prospect_id,
                        activity_key=activity_key,
                        activity_event=payload.get("ActivityEvent"),
                        message=result.get("message"),
                        payload_preview=payload,
                        response_preview=result.get("data"),
                    )
                except Exception:
                    logger.exception("[%s] Failed to write LeadSquared push log for %s", bid, call.get("callid"))
        except Exception as exc:
            logger.exception(
                "[%s] Exception while pushing LeadSquared activity for %s activity=%s",
                bid,
                call.get("callid"),
                activity_key,
            )
            try:
                db.log_crm_push(
                    bid=bid,
                    provider="leadsquared",
                    status="failed",
                    callid=call.get("callid"),
                    lead_phone=lead_phone,
                    crm_lead_id=prospect_id,
                    activity_key=activity_key,
                    message=str(exc),
                )
            except Exception:
                logger.exception("[%s] Failed to write LeadSquared push log for %s", bid, call.get("callid"))


# Backward-compatible aliases used by older scripts.
_lsq_build_activity_payload = build_lsq_activity_payload
_lsq_resolve_prospect_id = resolve_lsq_prospect_id
_push_leadsquared_activities = push_leadsquared_activities
