package api

import (
	"context"
	"encoding/json"
	"log/slog"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/TopherMayor/unified-media-manager/internal/service"
	"github.com/labstack/echo/v4"
)

func listNotificationChannels(svc *service.NotificationService) echo.HandlerFunc {
	return func(c echo.Context) error {
		ctx, cancel := context.WithTimeout(c.Request().Context(), 10*time.Second)
		defer cancel()

		channels, err := svc.ListChannels(ctx)
		if err != nil {
			slog.Error("failed to list notification channels", "error", err)
			return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to list channels"})
		}

		if channels == nil {
			channels = []service.NotificationChannel{}
		}
		return c.JSON(http.StatusOK, channels)
	}
}

type createNotificationChannelRequest struct {
	Name       string          `json:"name"`
	Type       string          `json:"type"`
	Config     json.RawMessage `json:"config"`
	Enabled    *bool           `json:"enabled,omitempty"`
	EventTypes []string        `json:"event_types"`
}

func createNotificationChannel(svc *service.NotificationService) echo.HandlerFunc {
	return func(c echo.Context) error {
		ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second)
		defer cancel()

		var req createNotificationChannelRequest
		if err := c.Bind(&req); err != nil {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		}

		name := strings.TrimSpace(req.Name)
		if len(name) < 3 || len(name) > 50 {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "name must be 3-50 characters"})
		}

		if req.Type != "webhook" && req.Type != "telegram" {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "type must be webhook or telegram"})
		}

		if req.Config == nil {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "config is required"})
		}

		id, err := svc.CreateChannel(ctx, name, req.Type, req.Config)
		if err != nil {
			slog.Error("failed to create notification channel", "error", err, "name", name, "type", req.Type)
			return c.JSON(http.StatusBadRequest, map[string]string{"error": err.Error()})
		}

		if len(req.EventTypes) > 0 {
			if subErr := svc.UpdateSubscriptions(ctx, id, req.EventTypes); subErr != nil {
				slog.Error("failed to set subscriptions", "error", subErr, "channel_id", id)
			}
		}

		return c.JSON(http.StatusCreated, map[string]int64{"id": id})
	}
}

type updateNotificationChannelRequest struct {
	Name       *string         `json:"name,omitempty"`
	Enabled    *bool           `json:"enabled,omitempty"`
	Config     json.RawMessage `json:"config,omitempty"`
	EventTypes []string        `json:"event_types,omitempty"`
}

func updateNotificationChannel(svc *service.NotificationService) echo.HandlerFunc {
	return func(c echo.Context) error {
		ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second)
		defer cancel()

		id, err := strconv.ParseInt(c.Param("id"), 10, 64)
		if err != nil {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid id"})
		}

		var req updateNotificationChannelRequest
		if err := c.Bind(&req); err != nil {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		}

		if err := svc.UpdateChannel(ctx, id, req.Name, req.Enabled, req.Config); err != nil {
			slog.Error("failed to update notification channel", "error", err, "id", id)
			return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()})
		}

		if req.EventTypes != nil {
			if subErr := svc.UpdateSubscriptions(ctx, id, req.EventTypes); subErr != nil {
				slog.Error("failed to update subscriptions", "error", subErr, "id", id)
				return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to update subscriptions"})
			}
		}

		return c.JSON(http.StatusOK, map[string]string{"status": "updated"})
	}
}

func deleteNotificationChannel(svc *service.NotificationService) echo.HandlerFunc {
	return func(c echo.Context) error {
		ctx, cancel := context.WithTimeout(c.Request().Context(), 5*time.Second)
		defer cancel()

		id, err := strconv.ParseInt(c.Param("id"), 10, 64)
		if err != nil {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid id"})
		}

		if err := svc.DeleteChannel(ctx, id); err != nil {
			slog.Error("failed to delete notification channel", "error", err, "id", id)
			return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()})
		}

		return c.JSON(http.StatusOK, map[string]string{"status": "deleted"})
	}
}

func testNotificationChannel(svc *service.NotificationService) echo.HandlerFunc {
	return func(c echo.Context) error {
		ctx, cancel := context.WithTimeout(c.Request().Context(), 15*time.Second)
		defer cancel()

		id, err := strconv.ParseInt(c.Param("id"), 10, 64)
		if err != nil {
			return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid id"})
		}

		ch, err := svc.GetChannelWithConfig(ctx, id)
		if err != nil {
			return c.JSON(http.StatusNotFound, map[string]string{"error": "channel not found"})
		}

		var configMap map[string]interface{}
		json.Unmarshal(ch.Config, &configMap)

		var deliverErr error
		switch ch.Type {
		case "webhook":
			webhookURL, _ := configMap["url"].(string)
			deliverErr = svc.DeliverWebhook(ctx, webhookURL, map[string]interface{}{
				"test":    true,
				"message": "Test notification from UMM",
			})

		case "telegram":
			botToken, _ := configMap["bot_token"].(string)
			chatID, _ := configMap["chat_id"].(string)
			deliverErr = svc.DeliverTelegram(ctx, botToken, chatID, "🔔 Test notification from UMM")
		}

		if deliverErr != nil {
			slog.Error("notification test failed", "channel", ch.Name, "type", ch.Type)
			return c.JSON(http.StatusOK, map[string]interface{}{
				"success": false,
				"error":   "delivery failed",
			})
		}

		return c.JSON(http.StatusOK, map[string]interface{}{
			"success": true,
		})
	}
}

func listNotificationQueue(svc *service.NotificationService) echo.HandlerFunc {
	return func(c echo.Context) error {
		ctx, cancel := context.WithTimeout(c.Request().Context(), 10*time.Second)
		defer cancel()

		status := c.QueryParam("status")
		page, pageSize := service.ParsePagination(c.QueryParam("page"), c.QueryParam("page_size"))

		entries, total, err := svc.ListQueue(ctx, status, page, pageSize)
		if err != nil {
			slog.Error("failed to list notification queue", "error", err)
			return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to list queue"})
		}

		if entries == nil {
			entries = []service.QueueEntry{}
		}

		totalPages := (total + pageSize - 1) / pageSize
		return c.JSON(http.StatusOK, paginatedResponse{
			Data:       entries,
			Total:      total,
			Page:       page,
			PageSize:   pageSize,
			TotalPages: totalPages,
		})
	}
}
