"""HTTP API server for gateway integrations."""

import asyncio
import json
from typing import Callable, Awaitable

from aiohttp import web
from loguru import logger

# Maximum allowed request body size (1MB)
_MAX_BODY_SIZE = 1024 * 1024


class GatewayServer:
    """
    HTTP API server for voice bridge and other integrations.

    Provides endpoints for:
    - Voice message handling (POST /api/voice/message)
    - Health check (GET /health)
    """

    def __init__(
        self,
        host: str = "127.0.0.1",
        port: int = 18790,
        on_voice_message: Callable[[str, str, str], Awaitable[str]] | None = None,
        on_cron_run: Callable[[str, bool], Awaitable[bool]] | None = None,
        on_cron_reload: Callable[[], Awaitable[int]] | None = None,
    ):
        """
        Initialize the gateway server.

        Args:
            host: Host to bind to.
            port: Port to listen on.
            on_voice_message: Callback for voice messages (call_sid, from, text) -> response.
            on_cron_run: Callback to trigger a cron job (job_id, force) -> success.
        """
        self.host = host
        self.port = port
        self.on_voice_message = on_voice_message
        self.on_cron_run = on_cron_run
        self.on_cron_reload = on_cron_reload
        self._app: web.Application | None = None
        self._runner: web.AppRunner | None = None
        self._site: web.TCPSite | None = None

    def _create_app(self) -> web.Application:
        """Create the aiohttp application."""
        app = web.Application(client_max_size=_MAX_BODY_SIZE)
        app.router.add_get("/health", self._handle_health)
        if self.on_voice_message:
            app.router.add_post("/api/voice/message", self._handle_voice_message)
        app.router.add_post("/api/cron/run", self._handle_cron_run)
        app.router.add_post("/api/cron/reload", self._handle_cron_reload)
        return app

    async def _handle_health(self, request: web.Request) -> web.Response:
        """Health check endpoint."""
        return web.json_response({"status": "ok"})

    async def _handle_cron_run(self, request: web.Request) -> web.Response:
        """Trigger a cron job by ID or name. POST /api/cron/run {"job_id": "...", "force": false}"""
        try:
            data = await request.json()
            job_id = data.get("job_id", "")
            force = bool(data.get("force", False))

            if not job_id or len(job_id) > 256:
                return web.json_response({"error": "Invalid job_id"}, status=400)

            if not self.on_cron_run:
                return web.json_response({"error": "Cron handler not configured"}, status=500)

            success = await self.on_cron_run(job_id, force)
            if success:
                return web.json_response({"ok": True})
            else:
                return web.json_response({"error": f"Job '{job_id}' not found or disabled"}, status=404)

        except json.JSONDecodeError:
            return web.json_response({"error": "Invalid JSON"}, status=400)
        except Exception as e:
            logger.error(f"Error triggering cron job: {e}")
            return web.json_response({"error": "Internal server error"}, status=500)

    async def _handle_cron_reload(self, request: web.Request) -> web.Response:
        """Reload cron jobs from disk. POST /api/cron/reload"""
        try:
            if not self.on_cron_reload:
                return web.json_response({"error": "Cron reload not configured"}, status=500)
            count = await self.on_cron_reload()
            return web.json_response({"ok": True, "jobs": count})
        except Exception as e:
            logger.error(f"Error reloading cron jobs: {e}")
            return web.json_response({"error": "Internal server error"}, status=500)

    async def _handle_voice_message(self, request: web.Request) -> web.Response:
        """
        Handle incoming voice message from voice bridge.

        Expected JSON body:
        {
            "call_sid": "CA...",
            "from": "+1234567890",
            "text": "User's transcribed speech"
        }

        Returns:
        {
            "response": "Agent's response to speak"
        }
        """
        try:
            data = await request.json()
            call_sid = data.get("call_sid", "")
            from_number = data.get("from", "")
            text = data.get("text", "")

            # Input validation
            if not call_sid or len(call_sid) > 128:
                return web.json_response({"error": "Invalid call_sid"}, status=400)
            if len(text) > 50000:
                return web.json_response({"error": "Message too large"}, status=413)

            logger.info(f"Voice message from {from_number}: {text[:50]}...")

            if not self.on_voice_message:
                return web.json_response(
                    {"error": "Voice handler not configured"},
                    status=500
                )

            # Get response from agent with timeout
            response = await asyncio.wait_for(
                self.on_voice_message(call_sid, from_number, text),
                timeout=30.0,
            )

            return web.json_response({"response": response})

        except json.JSONDecodeError:
            return web.json_response(
                {"error": "Invalid JSON"},
                status=400
            )
        except asyncio.TimeoutError:
            logger.error("Voice message handler timeout")
            return web.json_response(
                {"error": "Handler timeout"},
                status=504
            )
        except Exception as e:
            logger.error(f"Error handling voice message: {e}")
            # Don't expose internal error details to client
            return web.json_response(
                {"error": "Internal server error"},
                status=500
            )

    async def start(self) -> None:
        """Start the HTTP server."""
        self._app = self._create_app()
        self._runner = web.AppRunner(self._app)
        await self._runner.setup()
        self._site = web.TCPSite(self._runner, self.host, self.port)
        await self._site.start()
        logger.info(f"Gateway API listening on http://{self.host}:{self.port}")

    async def stop(self) -> None:
        """Stop the HTTP server."""
        if self._site:
            await self._site.stop()
        if self._runner:
            await self._runner.cleanup()
        logger.info("Gateway API stopped")
