"""Plivo telephony provider package."""

import uuid
from typing import Any, Dict

import aiohttp
from fastapi import HTTPException
from loguru import logger

from api.services.telephony.registry import (
    ProviderSpec,
    ProviderUIField,
    ProviderUIMetadata,
    register,
)
from api.utils.common import get_backend_endpoints

from .config import PlivoConfigurationRequest
from .provider import PlivoProvider
from .transport import create_transport

PLIVO_API_BASE_URL = "https://api.plivo.com/v1"


def _config_loader(value: Dict[str, Any]) -> Dict[str, Any]:
    return {
        "provider": "plivo",
        "auth_id": value.get("auth_id"),
        "auth_token": value.get("auth_token"),
        "application_id": value.get("application_id"),
        "from_numbers": value.get("from_numbers", []),
    }


async def _ensure_application_id(
    credentials: Dict[str, Any],
    existing_credentials: Dict[str, Any] | None = None,
) -> Dict[str, Any]:
    """Auto-create a Plivo Application if one wasn't supplied.

    The application is created with our inbound dispatcher URL pre-set — the
    same URL ``configure_inbound`` would POST later — so inbound calls work
    immediately for any number bound to this application.
    """
    if credentials.get("application_id"):
        return credentials

    auth_id = credentials.get("auth_id")
    auth_token = credentials.get("auth_token")
    if not auth_id or not auth_token:
        return credentials

    backend_endpoint, _ = await get_backend_endpoints()
    inbound_url = f"{backend_endpoint}/api/v1/telephony/inbound/run"

    app_name = f"dograh-{uuid.uuid4().hex[:12]}"
    endpoint = f"{PLIVO_API_BASE_URL}/Account/{auth_id}/Application/"
    body = {
        "app_name": app_name,
        "answer_url": inbound_url,
        "answer_method": "POST",
        "hangup_url": "",
    }
    auth = aiohttp.BasicAuth(auth_id, auth_token)

    try:
        async with aiohttp.ClientSession() as session:
            async with session.post(endpoint, json=body, auth=auth) as response:
                response_text = await response.text()
                if response.status not in (200, 201, 202):
                    logger.error(
                        f"[Plivo] applicationCreate failed: "
                        f"HTTP {response.status} body={response_text}"
                    )
                    raise HTTPException(
                        status_code=response.status,
                        detail=(
                            f"Failed to auto-create Plivo Application: "
                            f"HTTP {response.status} {response_text}"
                        ),
                    )
                data = await response.json()
    except aiohttp.ClientError as e:
        logger.error(f"[Plivo] applicationCreate transport error: {e}")
        raise HTTPException(
            status_code=502,
            detail=f"Failed to reach Plivo to auto-create application: {e}",
        )

    created_id = data.get("app_id")
    if not created_id:
        logger.error(f"[Plivo] applicationCreate response missing app_id: {data}")
        raise HTTPException(
            status_code=502,
            detail=f"Plivo applicationCreate response missing app_id: {data}",
        )

    logger.info(
        f"[Plivo] auto-created Application '{app_name}' (id={created_id}) on "
        f"account {auth_id}"
    )
    return {**credentials, "application_id": str(created_id)}


_UI_METADATA = ProviderUIMetadata(
    display_name="Plivo",
    docs_url="https://docs.dograh.com/integrations/telephony/plivo",
    fields=[
        ProviderUIField(name="auth_id", label="Auth ID", type="text", sensitive=True),
        ProviderUIField(
            name="auth_token", label="Auth Token", type="password", sensitive=True
        ),
        ProviderUIField(
            name="application_id",
            label="Application ID",
            type="text",
            required=False,
            description=(
                "Plivo Application ID whose answer_url is updated when inbound "
                "workflows are attached to numbers on this account. Leave blank "
                "and we will auto-create one for you on save."
            ),
        ),
        ProviderUIField(
            name="from_numbers",
            label="Phone Numbers",
            type="string-array",
            description="E.164-formatted Plivo phone numbers used for outbound calls",
        ),
    ],
)


SPEC = ProviderSpec(
    name="plivo",
    provider_cls=PlivoProvider,
    config_loader=_config_loader,
    transport_factory=create_transport,
    transport_sample_rate=8000,
    config_request_cls=PlivoConfigurationRequest,
    ui_metadata=_UI_METADATA,
    account_id_credential_field="auth_id",
    preprocess_credentials_on_save=_ensure_application_id,
)


register(SPEC)


__all__ = [
    "SPEC",
    "PlivoConfigurationRequest",
    "PlivoProvider",
    "create_transport",
]
