package handler import ( "github.com/gofiber/fiber/v2" "github.com/google/uuid" "github.com/gosec/gsc-ops-api/internal/middleware" "github.com/gosec/gsc-ops-api/internal/service" "github.com/gosec/gsc-ops-api/pkg/types" ) // VoiceAgentHandler handles voice agent management endpoints type VoiceAgentHandler struct { svc *service.VoiceAgentService } // NewVoiceAgentHandler creates a new voice agent handler func NewVoiceAgentHandler(svc *service.VoiceAgentService) *VoiceAgentHandler { return &VoiceAgentHandler{svc: svc} } // ============================================================================ // Voice Agent Configs // ============================================================================ // ListConfigs handles GET /api/v1/voice-agents func (h *VoiceAgentHandler) ListConfigs(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) params := types.ListParams{ Limit: c.QueryInt("limit", 50), Offset: c.QueryInt("offset", 0), Search: c.Query("search"), } var tenantID *uuid.UUID if tid := c.Query("tenantId"); tid != "" { parsed, err := uuid.Parse(tid) if err != nil { apiErr := types.NewBadRequest("Invalid tenantId") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } tenantID = &parsed } configs, total, err := h.svc.ListConfigs(c.Context(), params, tenantID) if err != nil { apiErr := types.NewInternal(err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewPagedResponse(configs, total, params.Limit, params.Offset, reqID)) } // GetConfig handles GET /api/v1/voice-agents/:id func (h *VoiceAgentHandler) GetConfig(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) id, err := uuid.Parse(c.Params("id")) if err != nil { apiErr := types.NewBadRequest("Invalid voice agent config ID") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } config, err := h.svc.GetConfig(c.Context(), id) if err != nil { apiErr := types.NewNotFound("Voice agent config not found") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewDataResponse(config, reqID)) } // CreateConfig handles POST /api/v1/voice-agents func (h *VoiceAgentHandler) CreateConfig(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) var req types.VoiceAgentConfigCreate if err := c.BodyParser(&req); err != nil { apiErr := types.NewBadRequest("Invalid request body: " + err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } if req.TenantID == uuid.Nil || req.AgentID == uuid.Nil { apiErr := types.NewValidation("tenantId and agentId are required") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } config, err := h.svc.CreateConfig(c.Context(), &req) if err != nil { apiErr := types.NewInternal(err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.Status(fiber.StatusCreated).JSON(types.NewDataResponse(config, reqID)) } // UpdateConfig handles PUT /api/v1/voice-agents/:id func (h *VoiceAgentHandler) UpdateConfig(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) id, err := uuid.Parse(c.Params("id")) if err != nil { apiErr := types.NewBadRequest("Invalid voice agent config ID") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } var req types.VoiceAgentConfigUpdate if err := c.BodyParser(&req); err != nil { apiErr := types.NewBadRequest("Invalid request body: " + err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } config, err := h.svc.UpdateConfig(c.Context(), id, &req) if err != nil { apiErr := types.NewInternal(err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewDataResponse(config, reqID)) } // DeleteConfig handles DELETE /api/v1/voice-agents/:id func (h *VoiceAgentHandler) DeleteConfig(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) id, err := uuid.Parse(c.Params("id")) if err != nil { apiErr := types.NewBadRequest("Invalid voice agent config ID") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } if err := h.svc.DeleteConfig(c.Context(), id); err != nil { apiErr := types.NewNotFound("Voice agent config not found") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.SendStatus(fiber.StatusNoContent) } // ============================================================================ // Voice Sessions // ============================================================================ // ListSessions handles GET /api/v1/voice-agents/:id/sessions func (h *VoiceAgentHandler) ListSessions(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) agentID, err := uuid.Parse(c.Params("id")) if err != nil { apiErr := types.NewBadRequest("Invalid voice agent config ID") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } params := types.ListParams{ Limit: c.QueryInt("limit", 50), Offset: c.QueryInt("offset", 0), } sessions, total, err := h.svc.ListSessions(c.Context(), agentID, params) if err != nil { apiErr := types.NewInternal(err.Error()) return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewPagedResponse(sessions, total, params.Limit, params.Offset, reqID)) } // GetSession handles GET /api/v1/voice-agents/sessions/:sessionId func (h *VoiceAgentHandler) GetSession(c *fiber.Ctx) error { reqID := middleware.GetRequestID(c) sessionID, err := uuid.Parse(c.Params("sessionId")) if err != nil { apiErr := types.NewBadRequest("Invalid session ID") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } session, err := h.svc.GetSession(c.Context(), sessionID) if err != nil { apiErr := types.NewNotFound("Voice session not found") return c.Status(apiErr.Status).JSON(types.NewErrorResponse(apiErr, reqID)) } return c.JSON(types.NewDataResponse(session, reqID)) }