package handler

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

	"github.com/gin-gonic/gin"

	alertmodel "security-center-backend/internal/modules/alerts/model"
	"security-center-backend/internal/modules/alerts/service"
	"security-center-backend/internal/pkg/cache"
	"security-center-backend/internal/pkg/httpcache"
	"security-center-backend/internal/pkg/response"
	jwtpkg "security-center-backend/internal/service/jwt"
)

type Handler struct {
	alertSvc *service.Service
	runtime  *service.AlertRuntimeControl
	cache    *cache.Cache
}

func New(alertSvc *service.Service, runtime *service.AlertRuntimeControl, cacheLayer *cache.Cache) *Handler {
	return &Handler{alertSvc: alertSvc, runtime: runtime, cache: cacheLayer}
}

func (h *Handler) RegisterRoutes(rg *gin.RouterGroup) {
	group := rg.Group("/alerts")
	group.GET("/runtime/status", h.RuntimeStatus)
	group.POST("/runtime/start", h.StartRuntime)
	group.POST("/runtime/stop", h.StopRuntime)
	group.POST("/runtime/refresh", h.RefreshRuntime)
	group.POST("/test", h.TestSend)

	group.GET("/fields", h.Fields)

	group.GET("/rules", h.ListRules)
	group.POST("/rules", h.CreateRule)
	group.PUT("/rules/:id", h.UpdateRule)
	group.DELETE("/rules/:id", h.DeleteRule)
	group.POST("/rules/:id/run", h.RunRuleNow)

	group.GET("/runs", h.ListRuns)
	group.GET("/runs/:id/deliveries", h.ListDeliveries)

	group.POST("/templates/render", h.RenderTemplate)
}

func (h *Handler) RuntimeStatus(c *gin.Context) {
	if h.runtime == nil {
		response.Error(c, http.StatusServiceUnavailable, "alert runtime not configured")
		return
	}
	response.Success(c, h.runtime.Status())
}

func (h *Handler) RefreshRuntime(c *gin.Context) {
	if h.runtime == nil {
		response.Error(c, http.StatusServiceUnavailable, "alert runtime not configured")
		return
	}
	status, err := h.runtime.Refresh()
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	response.Success(c, status)
}

func (h *Handler) StartRuntime(c *gin.Context) {
	if h.runtime == nil {
		response.Error(c, http.StatusServiceUnavailable, "alert runtime not configured")
		return
	}
	status, err := h.runtime.Start()
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	response.Success(c, status)
}

func (h *Handler) StopRuntime(c *gin.Context) {
	if h.runtime == nil {
		response.Error(c, http.StatusServiceUnavailable, "alert runtime not configured")
		return
	}
	status := h.runtime.Stop()
	response.Success(c, status)
}

func (h *Handler) Fields(c *gin.Context) {
	entity := c.Query("entity")
	fields, err := h.alertSvc.GetEntityFields(c.Request.Context(), entity)
	if err != nil {
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}
	response.Success(c, fields)
}

func (h *Handler) ListRules(c *gin.Context) {
	userID := uint64(0)
	if claimsValue, exists := c.Get("current_user"); exists {
		if claims, ok := claimsValue.(*jwtpkg.Claims); ok && claims != nil {
			userID = claims.UserID
		}
	}
	cacheKey := ""
	if h.cache != nil {
		cacheKey = "api:v1:alerts:rules:u" + strconv.FormatUint(userID, 10)
		if cached, err := h.cache.Get(c.Request.Context(), cacheKey); err == nil && cached != "" {
			if splitAt := strings.IndexByte(cached, '\n'); splitAt > 0 {
				etag := cached[:splitAt]
				body := cached[splitAt+1:]
				c.Header("ETag", etag)
				c.Header("Cache-Control", "private, max-age=15")
				if httpcache.IfNoneMatchMatches(c.GetHeader("If-None-Match"), etag) {
					c.Status(http.StatusNotModified)
					return
				}
				c.Data(http.StatusOK, "application/json", []byte(body))
				return
			}
		}
	}

	items, err := h.alertSvc.ListRules(c.Request.Context())
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	itemFingerprints := make([]string, 0, len(items))
	for _, item := range items {
		itemFingerprints = append(itemFingerprints, strconv.FormatUint(item.ID, 10)+"@"+item.UpdatedAt.UTC().Format(time.RFC3339Nano))
	}
	etag := httpcache.Checksum(
		"alerts:rules",
		"u="+strconv.FormatUint(userID, 10),
		strings.Join(itemFingerprints, ","),
	)
	c.Header("ETag", etag)
	c.Header("Cache-Control", "private, max-age=15")
	if httpcache.IfNoneMatchMatches(c.GetHeader("If-None-Match"), etag) {
		c.Status(http.StatusNotModified)
		return
	}

	apiResp := response.APIResponse{Code: 0, Message: "success", Data: items, Timestamp: time.Now().Unix()}
	encoded, err := json.Marshal(apiResp)
	if err != nil {
		response.Error(c, http.StatusInternalServerError, "failed to encode response")
		return
	}
	if cacheKey != "" {
		_ = h.cache.Set(c.Request.Context(), cacheKey, etag+"\n"+string(encoded), 15*time.Second)
	}
	c.Data(http.StatusOK, "application/json", encoded)
}

func (h *Handler) CreateRule(c *gin.Context) {
	var rule alertmodel.LarkAlertRule
	if err := c.ShouldBindJSON(&rule); err != nil {
		response.Error(c, http.StatusBadRequest, "invalid payload")
		return
	}
	if err := h.alertSvc.CreateRule(c.Request.Context(), &rule); err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	if h.runtime != nil {
		_, _ = h.runtime.Refresh()
	}
	h.invalidateListCaches(c.Request.Context())
	response.Success(c, rule)
}

func (h *Handler) UpdateRule(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil || id == 0 {
		response.Error(c, http.StatusBadRequest, "invalid id")
		return
	}
	var payload alertmodel.LarkAlertRule
	if err := c.ShouldBindJSON(&payload); err != nil {
		response.Error(c, http.StatusBadRequest, "invalid payload")
		return
	}

	existing, err := h.alertSvc.GetRule(c.Request.Context(), uint64(id))
	if err != nil {
		response.Error(c, http.StatusNotFound, err.Error())
		return
	}
	existing.Name = payload.Name
	existing.Enabled = payload.Enabled
	existing.EntityType = payload.EntityType
	existing.CronExpr = payload.CronExpr
	existing.EventOnCreate = payload.EventOnCreate
	existing.EventOnUpdate = payload.EventOnUpdate
	existing.EventOnStatusChange = payload.EventOnStatusChange
	existing.EventOnSeverityChange = payload.EventOnSeverityChange
	existing.SendViaWebhook = payload.SendViaWebhook
	existing.SendViaApp = payload.SendViaApp
	existing.RecipientEmployeeIDs = payload.RecipientEmployeeIDs
	existing.RecipientTargetKeys = payload.RecipientTargetKeys
	existing.Conditions = payload.Conditions
	existing.CardTemplate = payload.CardTemplate
	existing.MaxMatchesPerRun = payload.MaxMatchesPerRun

	if err := h.alertSvc.UpdateRule(c.Request.Context(), existing); err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	if h.runtime != nil {
		_, _ = h.runtime.Refresh()
	}
	h.invalidateListCaches(c.Request.Context())
	response.Success(c, existing)
}

func (h *Handler) DeleteRule(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil || id == 0 {
		response.Error(c, http.StatusBadRequest, "invalid id")
		return
	}
	if err := h.alertSvc.DeleteRule(c.Request.Context(), uint64(id)); err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	if h.runtime != nil {
		_, _ = h.runtime.Refresh()
	}
	h.invalidateListCaches(c.Request.Context())
	response.Success(c, gin.H{"deleted": true})
}

func (h *Handler) RunRuleNow(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil || id == 0 {
		response.Error(c, http.StatusBadRequest, "invalid id")
		return
	}
	run, err := h.alertSvc.RunRuleNow(c.Request.Context(), uint64(id))
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	h.invalidateListCaches(c.Request.Context())
	response.Success(c, run)
}

func (h *Handler) ListRuns(c *gin.Context) {
	limit := 50
	if raw := strings.TrimSpace(c.Query("limit")); raw != "" {
		if n, err := strconv.Atoi(raw); err == nil && n > 0 {
			if n > 200 {
				n = 200
			}
			limit = n
		}
	}
	runs, err := h.alertSvc.ListRecentRuns(c.Request.Context(), limit)
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	itemFingerprints := make([]string, 0, len(runs))
	for _, run := range runs {
		itemFingerprints = append(itemFingerprints, strconv.FormatUint(run.ID, 10)+"@"+run.UpdatedAt.UTC().Format(time.RFC3339Nano))
	}
	etag := httpcache.Checksum("alerts:runs", "limit="+strconv.Itoa(limit), strings.Join(itemFingerprints, ","))
	c.Header("ETag", etag)
	c.Header("Cache-Control", "private, max-age=15")
	if httpcache.IfNoneMatchMatches(c.GetHeader("If-None-Match"), etag) {
		c.Status(http.StatusNotModified)
		return
	}
	response.Success(c, runs)
}

func (h *Handler) ListDeliveries(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil || id == 0 {
		response.Error(c, http.StatusBadRequest, "invalid id")
		return
	}
	limit := 200
	if raw := strings.TrimSpace(c.Query("limit")); raw != "" {
		if n, err := strconv.Atoi(raw); err == nil && n > 0 {
			if n > 500 {
				n = 500
			}
			limit = n
		}
	}
	items, err := h.alertSvc.ListDeliveriesByRun(c.Request.Context(), uint64(id), limit)
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	itemFingerprints := make([]string, 0, len(items))
	for _, delivery := range items {
		itemFingerprints = append(itemFingerprints, strconv.FormatUint(delivery.ID, 10)+"@"+delivery.UpdatedAt.UTC().Format(time.RFC3339Nano))
	}
	etag := httpcache.Checksum(
		"alerts:deliveries",
		"run_id="+strconv.FormatUint(id, 10),
		"limit="+strconv.Itoa(limit),
		strings.Join(itemFingerprints, ","),
	)
	c.Header("ETag", etag)
	c.Header("Cache-Control", "private, max-age=15")
	if httpcache.IfNoneMatchMatches(c.GetHeader("If-None-Match"), etag) {
		c.Status(http.StatusNotModified)
		return
	}
	response.Success(c, items)
}

func (h *Handler) TestSend(c *gin.Context) {
	var payload struct {
		EmployeeID   *uint64 `json:"employee_id"`
		SendWebhook  bool    `json:"send_webhook"`
		CardTemplate string  `json:"card_template"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		response.Error(c, http.StatusBadRequest, "invalid payload")
		return
	}
	run, err := h.alertSvc.TestSend(c.Request.Context(), payload.EmployeeID, payload.SendWebhook, payload.CardTemplate)
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	h.invalidateListCaches(c.Request.Context())
	response.Success(c, run)
}

func (h *Handler) invalidateListCaches(ctx context.Context) {
	if h.cache == nil {
		return
	}
	_ = h.cache.InvalidateByPattern(ctx, "api:v1:alerts:*")
}

func (h *Handler) RenderTemplate(c *gin.Context) {
	var payload struct {
		EntityType   string  `json:"entity_type"`
		EntityID     *uint64 `json:"entity_id"`
		CardTemplate string  `json:"card_template"`
	}
	if err := c.ShouldBindJSON(&payload); err != nil {
		response.Error(c, http.StatusBadRequest, "invalid payload")
		return
	}
	card, err := h.alertSvc.ValidateAndRenderTemplate(c.Request.Context(), payload.EntityType, payload.EntityID, payload.CardTemplate)
	if err != nil {
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}
	response.Success(c, card)
}
