package alert

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

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"github.com/prometheus/alertmanager/template"
	"github.com/tidbcloud/account/idl/pbgen/proto/account"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"go.uber.org/zap"

	"github.com/pingcap-inc/clinic_cloud_server/pkg/alert/notification"
	"github.com/pingcap-inc/clinic_cloud_server/pkg/common"
	"github.com/pingcap-inc/clinic_cloud_server/pkg/entity"
)

func (s *Service) ListAlertNotifications(c *gin.Context) {
}

// alertmanagerMessage is what Alertmanager's webhook notifier actually posts.
// template.Data does not carry groupKey (it lives on notify/webhook.Message),
// so bind a wrapper to keep it.
type alertmanagerMessage struct {
	template.Data
	GroupKey string `json:"groupKey"`
}

//	curl --location 'clinic-dev-1923272735.us-west-2.elb.amazonaws.com:8085/api/alertInternal/v1/alertNotification' \
//	  --header 'Content-Type: application/json' \
//	  --data '{
//	    "status": "firing",
//	    "alerts": [
//	        {
//	            "status": "firing",
//	            "startsAt": "2024-11-11T00:21:08+00:00",
//	            "labels": {
//	                "tidb_cluster_id": "10832315937079846455",
//	                "o11y_biz_type": "dev",
//	                "severity": "warn"
//	            },
//	            "annotations": {
//	                "description": "test description",
//	                "message": "test message"
//	            }
//	        }
//	    ]
//	}'
func (s *Service) PushAlertNotification(c *gin.Context) {
	var message alertmanagerMessage
	if err := c.ShouldBindJSON(&message); err != nil {
		common.FailWithCode(c, http.StatusBadRequest, err.Error())
		return
	}
	s.PushAlerts(c, message.Data, message.GroupKey)
	common.Success(c, nil)
}

// groupKey may be empty when the caller does not have one, e.g. the BYOC
// distributor rebuilds template.Data itself.
func (s *Service) PushAlerts(c context.Context, payload template.Data, groupKey string) {
	for _, alert := range payload.Alerts {
		cluster, project, clusterType, err := s.getClusterAndProjectFromAlert(c, alert)
		if err != nil {
			log.Error("failed to get cluster and project from alert",
				zap.Error(err),
				zap.Any("alert", alert),
			)
			continue
		}
		targetId := cluster.ClusterId
		if clusterType == entity.ClusterDeployTypeDedicated {
			targetId = project.Id
		}
		subscribers, err := s.listAlertSubscribers(c, cluster.OrganizationId, targetId, clusterType, false)
		if err != nil {
			log.Error("failed to list alert subscribers", zap.Error(err),
				zap.String("cluster_type", string(clusterType)),
				zap.Uint64("cluster_id", cluster.ClusterId),
				zap.Uint64("tenant_id", cluster.OrganizationId),
				zap.Uint64("project_id", cluster.ProjectId),
			)
			continue
		}
		if len(subscribers) == 0 {
			log.Warn("no subscribers found for alert",
				zap.String("cluster_type", string(clusterType)),
				zap.Uint64("cluster_id", cluster.ClusterId),
				zap.Uint64("tenant_id", cluster.OrganizationId),
				zap.Uint64("project_id", cluster.ProjectId),
			)
			continue
		}
		alertMeta := notification.AlertMeta{
			TenantId:    cluster.OrganizationId,
			ClusterId:   cluster.ClusterId,
			ClusterName: cluster.ClusterName,
			GroupKey:    groupKey,
		}
		if clusterType == entity.ClusterDeployTypeDedicated && project != nil {
			alertMeta.ProjectId = project.Id
			alertMeta.ProjectName = project.Name
		}
		s.notificationClient.PushAsync(c,
			alert,
			subscribers,
			alertMeta,
			false)
	}
}

func (s *Service) getClusterAndProjectFromAlert(
	ctx context.Context,
	alert template.Alert,
) (*mgmtv1.Cluster, *account.Project, entity.ClusterDeployType, error) {
	clusterIDStr := alert.Labels[notification.AlertLabelTiDBClusterID]
	if clusterIDStr == "" {
		clusterIDStr = alert.Labels[notification.AlertLabelClusterID]
	}
	clusterID, err := strconv.ParseUint(clusterIDStr, 10, 64)
	if err != nil {
		return nil, nil, entity.ClusterDeployTypeUndefined,
			fmt.Errorf("failed to parse cluster_id from alert labels: %w, cluser_id: %s", err, clusterIDStr)
	}

	cluster, err := s.mgmtSvcClient.GetCluster(ctx, clusterID)
	if err != nil {
		return nil, nil, entity.ClusterDeployTypeUndefined,
			fmt.Errorf("failed to list cluster by id: %w, cluster_id: %d", err, clusterID)
	}

	if cluster.DeployType == mgmtv1.DeployType_DEPLOY_TYPE_DEDICATED {
		project, err := s.accountClient.GetProject(ctx, cluster.ProjectId)
		if err != nil {
			return nil, nil, entity.ClusterDeployTypeUndefined,
				fmt.Errorf("failed to get project by id: %w, project_id: %d", err, cluster.ProjectId)
		}
		return cluster, project, entity.ClusterDeployTypeDedicated, nil
	} else {
		return cluster, nil, entity.ClusterDeployTypeNextGen, nil
	}
}
