package api

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

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/prometheus/alertmanager/template"
	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	"github.com/tidbcloud/internal-portal/core-portal/central/db"
	"github.com/tidbcloud/internal-portal/db/entity"
	"github.com/tidbcloud/internal-portal/db/useralerts"
	"github.com/tidbcloud/internal-portal/temporary/grpcli"
	"github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

const (
	alertTimeFormatFull  = "2006-01-02 15:04:05 -07:00"
	alertTimeFormatShort = "01-02 15:04"
)

// FireAlert sends an alert to the user
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 != ALERT_STATUS_FIRING {
		Success(c, http.StatusOK, "")
		return
	}

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

	Success(c, http.StatusOK, "")
}

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))

	var (
		userEmails []string
		cluster    *core.Cluster
		project    *account.GetProjectByIDResponse
		clusterID  uint64
		err        error
	)

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

	err = db.Transaction(func(tx *gorm.DB) error {

		filter := &core.ClusterFilter{
			ClusterIds: []uint64{clusterID},
		}

		cluster, err = grpcli.GetCluster(ctx, &core.ListClustersReq{Filter: filter}, uint64(clusterID))
		if err != nil {
			return err
		}

		project, err = grpcli.GetProjectByID(ctx, &account.GetProjectByIDRequest{Id: cluster.ProjectId})
		if err != nil {
			return err
		}

		userAlertService := &useralerts.DefaultUserAlertsServiceImpl{}
		userAlerts, err := userAlertService.GetUserAlertsByProjectID(ctx, tx, entity.ID(project.Project.Id))
		if err != nil {
			return err
		}

		for _, i := range userAlerts {
			user, err := grpcli.GetUserByID(ctx, &account.GetUserByIDRequest{UserId: uint64(i.UserID)})
			if err != nil {
				logger.Error("no user info found, alert cannot be sent", zap.Int64("userID", int64(i.UserID)))
				continue
			}
			userEmails = append(userEmails, user.User.Email)
		}

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

	if len(userEmails) == 0 {
		logger.Error("no user emails found, alert cannot be sent", zap.Uint64("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
	}
	starts := alert.StartsAt

	content = strings.ReplaceAll(content, ClusterAnnotationToReplace, html.EscapeString(cluster.Name))
	content = strings.ReplaceAll(content, ClusterAnnotationToReplace, html.EscapeString(cluster.Name))
	content = strings.ReplaceAll(content, DomainAnnotationToReplace, html.EscapeString(genTiDBCloudDomain(alert)))
	content = strings.ReplaceAll(content, ProjectAnnotationToReplace, html.EscapeString(project.Project.Name))

	vars := map[string]string{
		"subject": fmt.Sprintf("%s at %s", subject, starts.Format(alertTimeFormatShort)),
		"content": fmt.Sprintf("Alert time: %s <br />Message: %s", starts.Format(alertTimeFormatFull), content),
	}

	// SendMailWithTemplate
	// mailFacade := utils.GetMailFacade()
	// err = mailFacade.SendMailWithTemplate(ctx, userEmails, subject, 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))

	// SendEmail with account grpc
	_, err = grpcli.SendEmail(ctx, &account.SendEmailReq{
		OrgId:        project.Project.TenantId,
		Scene:        NotificationSceneUserAlert,
		Identifier:   uuid.New().String(),
		Subject:      subject,
		Emails:       userEmails,
		TemplateId:   SendgridTemplateName2id[TNUserAlertIsTriggered],
		TemplateVars: vars,
	})

	if err != nil {
		logger.Error("send alert email to users failed", zap.String("subject", subject), zap.Any("emails", userEmails), zap.Any("alert", alert), zap.Error(err))
	} else {
		logger.Info("send alert email to users", zap.String("subject", subject), zap.Any("emails", userEmails), zap.Any("alert", alert))
	}
}

func genTiDBCloudDomain(alert template.Alert) string {

	o11yBizType := alert.Labels[AlertO11yBizTypeKeyName]

	switch {

	case strings.HasPrefix(o11yBizType, "dev"):
		return fmt.Sprintf("dev.%s", Domain)

	case strings.HasPrefix(o11yBizType, "staging"):
		return fmt.Sprintf("staging.%s", Domain)
	}
	return Domain
}
