#
# Copyright (c) 2025, Daily
#
# SPDX-License-Identifier: BSD 2-Clause License
#

"""server.py

Webhook server to handle outbound call requests, initiate calls via Twilio API,
and handle subsequent WebSocket connections for Media Streams.
"""

import os
from typing import Annotated

import uvicorn
from dotenv import load_dotenv
from fastapi import Depends, FastAPI, HTTPException, Request, WebSocket
from fastapi.responses import HTMLResponse
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from loguru import logger

from use_cases.resolve_salon_id import ResolveSalonIdUseCase
from server_utils import (
    DialoutRequest,
    DialoutResponse,
    generate_twiml,
    make_twilio_call,
    parse_twiml_request,
)

load_dotenv(override=True)


app = FastAPI()

bearer_auth = HTTPBearer(description="Salon JWT (NestJS HS256)")


@app.post("/dialout", response_model=DialoutResponse)
async def handle_dialout_request(
    payload: DialoutRequest,
    credentials: Annotated[HTTPAuthorizationCredentials, Depends(bearer_auth)],
) -> DialoutResponse:
    """Handle outbound call request and initiate call via Twilio.

    Args:
        payload: JSON body with `to_number` and `from_number` (E.164 recommended).
        credentials: Bearer token used to resolve the salon and authorize the call.

    Returns:
        DialoutResponse: Response containing call_sid, status, and to_number.

    Raises:
        HTTPException: If the token is invalid or Twilio call initiation fails.
    """
    logger.info("Received outbound call request")

    access_token = credentials.credentials
    try:
        salon_id = ResolveSalonIdUseCase().execute(access_token)
    except ValueError as e:
        raise HTTPException(status_code=401, detail=str(e)) from e

    logger.info(f"Authenticated salon {salon_id}")

    call_result = await make_twilio_call(payload, access_token)

    return DialoutResponse(
        call_sid=call_result.call_sid,
        status="call_initiated",
        to_number=call_result.to_number,
    )


@app.post("/twiml")
async def get_twiml(request: Request) -> HTMLResponse:
    """Return TwiML instructions for connecting call to WebSocket.

    This endpoint is called by Twilio when a call is initiated. It returns TwiML
    that instructs Twilio to connect the call to our WebSocket endpoint with
    stream parameters containing call metadata.

    Args:
        request (Request): FastAPI request containing Twilio form data with 'To' and 'From'.

    Returns:
        HTMLResponse: TwiML XML response with Stream connection instructions.
    """
    logger.info("Serving TwiML for outbound call")

    twiml_request = await parse_twiml_request(request)

    twiml_content = generate_twiml(twiml_request)

    return HTMLResponse(content=twiml_content, media_type="application/xml")


@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    """Handle WebSocket connection from Twilio Media Streams.

    This endpoint receives the WebSocket connection from Twilio's Media Streams
    and runs the bot to handle the voice conversation. Stream parameters passed
    from TwiML are available to the bot for customization.

    Args:
        websocket (WebSocket): FastAPI WebSocket connection from Twilio.
    """
    from bot import bot
    from pipecat.runner.types import WebSocketRunnerArguments

    await websocket.accept()
    logger.info("WebSocket connection accepted for outbound call")

    try:
        runner_args = WebSocketRunnerArguments(websocket=websocket)
        await bot(runner_args)
    except Exception as e:
        logger.error(f"Error in WebSocket endpoint: {e}")
        await websocket.close()


if __name__ == "__main__":
    # Run the server
    port = int(os.getenv("PORT", "7860"))
    logger.info(f"Starting Twilio outbound chatbot server on port {port}")
    uvicorn.run(app, host="0.0.0.0", port=port)
