import json
import logging
from typing import Any

from pydantic import BaseModel, Field
from fastapi import APIRouter, Depends, Request

from app.core.config import Settings, get_settings
from app.services.auto_reply_service import build_reply_payload
from app.services.closure_survey_service import (
    build_closure_survey_answer_reply,
    build_closure_survey_start_reply,
    is_resolution_event,
    mark_closure_survey_started,
)
from app.services.customer_state_service import build_customer_intake_reply
from app.services.exceptions import FreshchatAPIError
from app.services.freshchat_client import FreshchatClient
from app.services.freshchat_webhook_service import (
    extract_actor_id,
    extract_actor_type,
    extract_channel_id,
    extract_conversation_id,
    extract_customer_id,
    extract_event_type,
    extract_freshchat_conversation_id,
    extract_message_id,
    extract_message_text,
    should_ignore_event,
    validate_webhook_signature,
)

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/freshchat", tags=["freshchat-webhook"])


class TestSendMessageRequest(BaseModel):
    conversation_id: str = Field(min_length=1)
    message: str = Field(min_length=1)


class TestBuildReplyRequest(BaseModel):
    message: str = Field(min_length=1)


class TestSendQuickRepliesRequest(BaseModel):
    conversation_id: str = Field(min_length=1)
    message: str = Field(min_length=1)
    buttons: list[str] = Field(min_length=1)
    help_text: str | None = None
    display_order_style: str | None = None


@router.post("/webhook")
async def freshchat_webhook(
    request: Request,
    settings: Settings = Depends(get_settings),
) -> dict[str, Any]:
    headers = dict(request.headers)
    raw_body = await request.body()

    try:
        payload: dict[str, Any] = json.loads(raw_body.decode("utf-8")) if raw_body else {}
    except json.JSONDecodeError:
        payload = {"_raw_body": raw_body.decode("utf-8", errors="replace")}

    signature_valid = validate_webhook_signature(headers, raw_body, settings)
    event_type = extract_event_type(payload)
    actor_id = extract_actor_id(payload)
    actor_type = extract_actor_type(payload)
    customer_id = extract_customer_id(payload)
    conversation_id = extract_conversation_id(payload)
    freshchat_conversation_id = extract_freshchat_conversation_id(payload)
    message_id = extract_message_id(payload)
    channel_id = extract_channel_id(payload)
    message_text = extract_message_text(payload)
    ignored = should_ignore_event(payload)

    logger.debug("Freshchat webhook headers=%s", _headers_for_log(headers))
    logger.debug("Freshchat webhook payload=%s", payload)
    logger.debug(
        (
            "Freshchat webhook extracted event_type=%s actor_type=%s "
            "actor_id=%s customer_id=%s conversation_id=%s freshchat_conversation_id=%s message_id=%s "
            "channel_id=%s message_text=%r ignored=%s signature_valid=%s"
        ),
        event_type,
        actor_type,
        actor_id,
        customer_id,
        conversation_id,
        freshchat_conversation_id,
        message_id,
        channel_id,
        message_text,
        ignored,
        signature_valid,
    )

    base_data: dict[str, Any] = {
        "received": True,
        "ignored": ignored,
        "event_type": event_type,
        "actor_id": actor_id,
        "actor_type": actor_type,
        "customer_id": customer_id,
        "conversation_id": conversation_id,
        "freshchat_conversation_id": freshchat_conversation_id,
        "message_id": message_id,
        "channel_id": channel_id,
        "message_text": message_text,
    }

    if settings.freshchat_auto_reply_enabled and is_resolution_event(event_type, payload):
        if not conversation_id:
            logger.warning("Closure survey skipped because conversation_id is missing")
            return {
                "error": "false",
                "mensaje": "Webhook de cierre recibido sin conversation_id",
                "response_type": "text",
                "data": {
                    **base_data,
                    "ignored": False,
                    "reply_sent": False,
                    "missing_conversation_id": True,
                },
            }

        try:
            client = _build_client(settings)
            reply_payload = build_closure_survey_start_reply(
                settings.customer_state_store_path,
                conversation_id,
                backend=settings.customer_state_backend,
                mysql_config=_customer_state_mysql_config(settings),
                customer_key=customer_id,
            )
            if reply_payload is None:
                logger.info("Closure survey already sent conversation_id=%s", conversation_id)
                return {
                    "error": "false",
                    "mensaje": "Webhook de cierre recibido; encuesta ya enviada",
                    "response_type": "text",
                    "data": {
                        **base_data,
                        "ignored": False,
                        "reply_sent": False,
                        "closure_survey_already_sent": True,
                    },
                }

            reply_type, fallback_used, freshchat_response = await _send_reply_payload(
                client,
                conversation_id,
                reply_payload,
            )
            mark_closure_survey_started(
                settings.customer_state_store_path,
                conversation_id,
                backend=settings.customer_state_backend,
                mysql_config=_customer_state_mysql_config(settings),
                customer_key=customer_id,
            )
        except Exception as exc:
            logger.exception("Freshchat closure survey sending failed")
            return {
                "error": "true",
                "mensaje": "Webhook de cierre recibido, pero no se pudo enviar encuesta",
                "response_type": "text",
                "data": {
                    **base_data,
                    "reply_sent": False,
                    "send_error": _format_send_error(exc),
                },
            }

        return {
            "error": "false",
            "mensaje": "Webhook de cierre recibido y encuesta enviada",
            "response_type": "text",
            "data": {
                **base_data,
                "ignored": False,
                "reply_sent": True,
                "reply_type": reply_type,
                "fallback_used": fallback_used,
                "reply_payload": reply_payload,
                "freshchat_response": freshchat_response,
            },
        }

    if ignored:
        logger.info("Ignored Freshchat event action=%s actor_type=%s", event_type, actor_type)
        return {
            "error": "false",
            "mensaje": "Webhook recibido e ignorado",
            "response_type": "text",
            "data": base_data,
        }

    logger.info("Freshchat webhook headers=%s", _headers_for_log(headers))
    logger.info("Freshchat webhook payload=%s", payload)
    logger.info(
        (
            "Freshchat webhook extracted event_type=%s actor_type=%s "
            "actor_id=%s customer_id=%s conversation_id=%s freshchat_conversation_id=%s message_id=%s "
            "channel_id=%s message_text=%r ignored=%s signature_valid=%s"
        ),
        event_type,
        actor_type,
        actor_id,
        customer_id,
        conversation_id,
        freshchat_conversation_id,
        message_id,
        channel_id,
        message_text,
        ignored,
        signature_valid,
    )

    if not settings.freshchat_auto_reply_enabled:
        return {
            "error": "false",
            "mensaje": "Webhook recibido sin auto respuesta",
            "response_type": "text",
            "data": {
                **base_data,
                "reply_sent": False,
                "auto_reply_enabled": False,
            },
        }

    try:
        client = _build_client(settings)
        customer_key = customer_id or actor_id or freshchat_conversation_id or conversation_id or ""
        reply_payload = build_closure_survey_answer_reply(
            settings.customer_state_store_path,
            conversation_id or "",
            message_text or "",
            backend=settings.customer_state_backend,
            mysql_config=_customer_state_mysql_config(settings),
            customer_key=customer_key,
        )
        if reply_payload is None:
            reply_payload = build_customer_intake_reply(
                settings.customer_state_store_path,
                customer_key,
                message_text or "",
                backend=settings.customer_state_backend,
                mysql_config=_customer_state_mysql_config(settings),
            )
        if reply_payload is None:
            reply_payload = build_reply_payload(message_text or "")
        reply_type = str(reply_payload["type"])
        logger.info(
            (
                "Freshchat webhook reply generated conversation_id=%s customer_key=%s "
                "message_text=%r reply_type=%s intake=%s survey=%s flow=%s field=%s"
            ),
            conversation_id,
            customer_key,
            message_text,
            reply_type,
            bool(reply_payload.get("intake")),
            bool(reply_payload.get("survey")),
            reply_payload.get("flow"),
            reply_payload.get("field"),
        )

        reply_type, fallback_used, freshchat_response = await _send_reply_payload(
            client,
            conversation_id or "",
            reply_payload,
        )
        auto_resolve_response = None
        if reply_payload.get("survey_action") in {"completed", "declined"} and conversation_id:
            try:
                auto_resolve_response = await client.resolve_conversation(conversation_id)
            except Exception as resolve_exc:
                logger.warning(
                    "Freshchat auto resolve after survey failed conversation_id=%s error=%s",
                    conversation_id,
                    _format_send_error(resolve_exc),
                )
    except Exception as exc:
        logger.exception("Freshchat webhook received event but response sending failed")
        return {
            "error": "true",
            "mensaje": "Webhook recibido, pero no se pudo enviar respuesta",
            "response_type": "text",
            "data": {
                **base_data,
                "reply_sent": False,
                "reply_type": locals().get("reply_type"),
                "fallback_used": locals().get("fallback_used", False),
                "send_error": _format_send_error(exc),
            },
        }

    return {
        "error": "false",
        "mensaje": "Webhook recibido y respuesta enviada",
        "response_type": "text",
        "data": {
            **base_data,
            "ignored": False,
            "reply_sent": True,
            "reply_type": reply_type,
            "fallback_used": fallback_used,
            "customer_key": locals().get("customer_key"),
            "reply_payload": reply_payload,
            "freshchat_response": freshchat_response,
            "auto_resolve_response": locals().get("auto_resolve_response"),
        },
    }


async def _send_reply_payload(
    client: FreshchatClient,
    conversation_id: str,
    reply_payload: dict[str, Any],
) -> tuple[str, bool, dict[str, Any]]:
    reply_type = str(reply_payload["type"])
    fallback_used = False
    if reply_type == "quick_replies":
        try:
            freshchat_response = await client.send_quick_reply_message(
                conversation_id=conversation_id,
                text=str(reply_payload["text"]),
                buttons=list(reply_payload["buttons"]),
                help_text=str(reply_payload.get("help_text") or "Selecciona una opcion"),
                display_order_style="snake",
            )
        except Exception:
            logger.exception(
                "Freshchat quick replies failed, sending text fallback conversation_id=%s",
                conversation_id,
            )
            fallback_used = True
            fallback_text = str(reply_payload.get("fallback_text") or reply_payload["text"])
            freshchat_response = await client.send_text_message(conversation_id, fallback_text)
            reply_type = "text"
            reply_payload.clear()
            reply_payload.update({"type": "text", "text": fallback_text})
    elif reply_type == "dropdown":
        try:
            freshchat_response = await client.send_dropdown_message(
                conversation_id=conversation_id,
                text=str(reply_payload["text"]),
                options=list(reply_payload["buttons"]),
                help_text=str(reply_payload.get("help_text") or "Selecciona una opcion"),
            )
        except Exception:
            logger.exception(
                "Freshchat dropdown failed, sending text fallback conversation_id=%s",
                conversation_id,
            )
            fallback_used = True
            fallback_text = str(reply_payload.get("fallback_text") or reply_payload["text"])
            freshchat_response = await client.send_text_message(conversation_id, fallback_text)
            reply_type = "text"
            reply_payload.clear()
            reply_payload.update({"type": "text", "text": fallback_text})
    else:
        freshchat_response = await client.send_text_message(
            conversation_id,
            str(reply_payload["text"]),
        )

    return reply_type, fallback_used, freshchat_response


def _customer_state_mysql_config(settings: Settings) -> dict[str, Any]:
    return {
        "host": settings.customer_state_mysql_host,
        "port": settings.customer_state_mysql_port,
        "database": settings.customer_state_mysql_database,
        "user": settings.customer_state_mysql_user,
        "password": settings.customer_state_mysql_password,
        "table": settings.customer_state_mysql_table,
    }


@router.get("/test/config")
def freshchat_test_config(settings: Settings = Depends(get_settings)) -> dict[str, Any]:
    return {
        "status": "ok",
        "freshchat_api_base_url_configured": bool(settings.freshchat_api_base_url),
        "freshchat_api_token_configured": bool(settings.freshchat_api_token and settings.freshchat_api_token != "change-me"),
        "freshchat_auto_reply_enabled": settings.freshchat_auto_reply_enabled,
        "freshchat_send_as_actor_type": settings.freshchat_send_as_actor_type,
        "customer_state_backend": settings.customer_state_backend,
        "customer_state_store_path": settings.customer_state_store_path,
        "customer_state_mysql_host_configured": bool(settings.customer_state_mysql_host),
        "customer_state_mysql_database_configured": bool(settings.customer_state_mysql_database),
        "customer_state_mysql_user_configured": bool(settings.customer_state_mysql_user),
        "customer_state_mysql_table": settings.customer_state_mysql_table,
    }


@router.post("/test/build-reply")
def freshchat_test_build_reply(request: TestBuildReplyRequest) -> dict[str, Any]:
    reply_payload = build_reply_payload(request.message)
    return {
        "error": "false",
        "mensaje": "Respuesta construida",
        "response_type": "text",
        "data": {
            "reply_payload": reply_payload,
        },
    }


@router.post("/test/send-message")
async def freshchat_test_send_message(
    request: TestSendMessageRequest,
    settings: Settings = Depends(get_settings),
) -> dict[str, Any]:
    try:
        client = _build_client(settings)
        response = await client.send_text_message(request.conversation_id, request.message)
    except Exception as exc:
        logger.exception("Manual Freshchat send-message test failed")
        return {
            "error": "true",
            "mensaje": "No se pudo enviar mensaje",
            "response_type": "text",
            "data": {
                "conversation_id": request.conversation_id,
                "message": request.message,
                "send_error": _format_send_error(exc),
            },
        }

    return {
        "error": "false",
        "mensaje": "Mensaje enviado",
        "response_type": "text",
        "data": {
            "conversation_id": request.conversation_id,
            "message": request.message,
            "freshchat_response": response,
        },
    }


@router.post("/test/send-quick-replies")
async def freshchat_test_send_quick_replies(
    request: TestSendQuickRepliesRequest,
    settings: Settings = Depends(get_settings),
) -> dict[str, Any]:
    clean_buttons = [button.strip() for button in request.buttons if button and button.strip()][:3]
    clean_help_text = request.help_text.strip() if request.help_text else "Selecciona una opción"
    if not clean_help_text:
        clean_help_text = "Selecciona una opción"
    display_order_style = request.display_order_style or "snake"
    if display_order_style not in {"snake", "camel"}:
        display_order_style = "snake"
    try:
        client = _build_client(settings)
        response = await client.send_quick_reply_message(
            request.conversation_id,
            request.message,
            clean_buttons,
            clean_help_text,
            display_order_style,
        )
    except Exception as exc:
        logger.exception("Manual Freshchat send-quick-replies test failed")
        return {
            "error": "true",
            "mensaje": "No se pudo enviar mensaje con quick replies",
            "response_type": "text",
            "data": {
                "conversation_id": request.conversation_id,
                "send_error": _format_send_error(exc),
                "status_code": _error_status_code(exc),
                "response_body": _error_response_body(exc),
            },
        }

    return {
        "error": "false",
        "mensaje": "Mensaje con quick replies enviado",
        "response_type": "text",
        "data": {
            "conversation_id": request.conversation_id,
            "message": request.message,
            "help_text": clean_help_text,
            "display_order_style": display_order_style,
            "buttons": clean_buttons,
            "freshchat_response": response,
        },
    }


def _headers_for_log(headers: dict[str, str]) -> dict[str, str]:
    sensitive = {"authorization", "cookie", "set-cookie", "x-api-key"}
    return {
        key: ("<redacted>" if key.lower() in sensitive else value)
        for key, value in headers.items()
    }


def _build_client(settings: Settings) -> FreshchatClient:
    if not settings.freshchat_api_base_url:
        raise FreshchatAPIError("FRESHCHAT_API_BASE_URL no esta configurado.")
    if not settings.freshchat_api_token or settings.freshchat_api_token == "change-me":
        raise FreshchatAPIError("FRESHCHAT_API_TOKEN no esta configurado.")
    return FreshchatClient(
        base_url=settings.freshchat_api_base_url,
        token=settings.freshchat_api_token,
        actor_type=settings.freshchat_send_as_actor_type,
    )


def _format_send_error(exc: Exception) -> str:
    if isinstance(exc, FreshchatAPIError):
        details = [exc.message]
        if exc.status_code is not None:
            details.append(f"status_code={exc.status_code}")
        if exc.response_body:
            details.append(f"response_body={exc.response_body[:1000]}")
        return " | ".join(details)
    return str(exc)


def _error_status_code(exc: Exception) -> int | None:
    if isinstance(exc, FreshchatAPIError):
        return exc.status_code
    return None


def _error_response_body(exc: Exception) -> str | None:
    if isinstance(exc, FreshchatAPIError):
        return exc.response_body
    return None

