package api

import (
	"context"
	"html"
	"net/http"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/dbaas/admin/consts"
	"github.com/pingcap/dbaas/central/config"
	"github.com/pingcap/dbaas/central/db"
	"github.com/pingcap/dbaas/central/db/dbutil"
	"github.com/pingcap/dbaas/central/db/dbutil/useralerts"
	"github.com/pingcap/dbaas/central/db/entity"
	"github.com/pingcap/dbaas/internalportal/utils"
	"github.com/prometheus/alertmanager/template"
	"github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

func sendAlertToUsers(ctx context.Context, alert template.Alert) {
	logger := log.WithContextL(ctx)
	logger.Info("received alert", zap.Any("labels", alert.Labels), zap.Any("annotations", alert.Annotations))

	clusterID, err := strconv.ParseInt(alert.Labels[AlertClusterIDKeyName], 10, 64)
	if err != nil {
		logger.Error("failed to parse cluster_id from alert labels", zap.Any("alert", alert))
		return
	}

	var (
		userEmails []string
		cluster    entity.Cluster
	)

	err = db.Transaction(func(tx *gorm.DB) error {
		cluster, err = dbutil.GetCluster(tx, entity.ID(clusterID))
		if err != nil {
			return err
		}

		userAlertService := &useralerts.DefaultUserAlertsServiceImpl{}

		userAlerts, err := userAlertService.GetUserAlertsByProjectID(ctx, tx, cluster.ProjectID)
		if err != nil {
			return err
		}

		for _, i := range userAlerts {
			userEmails = append(userEmails, i.User.Email)
		}

		return nil
	})
	if err != nil {
		logger.Error("failed to query cluster by clusterID", zap.Int64("clusterID", clusterID), zap.Error(err))
		return
	}

	if len(userEmails) == 0 {
		logger.Error("no user emails found, alert cannot be sent", zap.Int64("clusterID", clusterID))
		return
	}

	subject := alert.Annotations[AlertSubjectKeyName]
	content := alert.Annotations[AlertContentKeyName]
	if subject == "" || content == "" {
		logger.Error("bad subject or content, so this alert will be skipped", zap.String("subject", subject), zap.String("content", content))
		return
	}

	content = strings.ReplaceAll(content, ClusterAnnotationToReplace, html.EscapeString(cluster.Name))
	content = strings.ReplaceAll(content, ProjectAnnotationToReplace, html.EscapeString(cluster.Project.Name))
	vars := map[string]string{"subject": subject, "content": content}

	// SendMailWithTemplate
	mailFacade := utils.GetMailFacade()
	err = mailFacade.SendMailWithTemplate(ctx, userEmails, subject, config.TNUserAlertIsTriggered, vars, nil)
	logger.Info("send alert email to users", zap.String("subject", subject), zap.Any("emails", userEmails), zap.Any("alert", alert), zap.String("clusterName", cluster.Name), zap.Error(err))
}

// @Summary FireAlert
// @tags Alerts
// @Accept  json
// @Produce json
// @Param role body template.Data true "FireAlert"
// @Success 200 {object} forms.EmptyResponse
// @Router /alerts [post]
func FireAlert(c *gin.Context) {
	var payload template.Data
	if err := c.ShouldBindJSON(&payload); err != nil {
		Fail(c, err, http.StatusBadRequest)
		return
	}
	// payload example here: https://github.com/pingcap/dead-mans-switch/blob/master/manifest/payload.json
	if payload.Status != consts.ALERT_STATUS_FIRING {
		Success(c, nil, http.StatusOK)
		return
	}

	for _, alert := range payload.Alerts {
		sendAlertToUsers(c.Request.Context(), alert)
	}

	Success(c, nil, http.StatusOK)
}
