package entity

import (
	"context"
	"strconv"
	"time"

	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"

	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
	"github.com/tidbcloud/tidb-management-service/utilities/clusterevent"
	"github.com/tidbcloud/tidb-management-service/utilities/grpc/metadata"
)

func NewClusterEvent(
	ctx context.Context,
	c *Cluster,
	eventType clusterevent.EventType,
	status string,
) (*clusterevent.ClusterEvent, bizerr.BizError) {
	if c == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "cluster cannot be nil")
	}

	var (
		operatorId       uint64
		email            string
		openAPIPublicKey string
	)

	reqMeta, err := metadata.ExtractRequestMeta(ctx)
	if err != nil {
		tilog.Warn(ctx, "extract request meta for cluster event", zap.Error(err))
	} else {
		operatorId, err = strconv.ParseUint(reqMeta.UserID, 10, 64)
		if err != nil {
			tilog.Warn(ctx, "parse uint failed", zap.String("id", reqMeta.UserID), zap.Error(err))
		}
		email = reqMeta.Email
		openAPIPublicKey = reqMeta.ApiPublicKey
	}

	return &clusterevent.ClusterEvent{
		TenantID:         c.OrganizationId,
		ProjectID:        0,
		ClusterID:        c.ClusterId,
		TraceID:          clusterevent.TraceIDFromContext(ctx),
		OperatorID:       operatorId,
		UserEmail:        email,
		OpenAPIPublicKey: openAPIPublicKey,
		IsDevTier:        false,
		Status:           status,
		EventType:        eventType,
		EventCreatedAtMs: time.Now().UnixMilli(),
		ClusterType:      clusterevent.ClusterTypeTidbxInstance,
		ExtraInfo: &clusterevent.EventExtraInfo{
			DurationSeconds: nil,
		},
		ServicePlan: c.ServicePlan.String(),
	}, nil
}

// NewResourcePoolClusterInstanceEvent creates a tidb_x_resource_pool cluster event to record that
// an instance was created or deleted on the resource pool. The ClusterID is set to the resource
// pool's ID (parsed from cluster.SharedPoolId), NOT the instance's ID.
func NewResourcePoolClusterInstanceEvent(
	ctx context.Context,
	c *Cluster,
	eventType clusterevent.EventType,
	status string,
) (*clusterevent.ClusterEvent, bizerr.BizError) {
	if c == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "cluster cannot be nil")
	}

	resourcePoolId, parseErr := strconv.ParseUint(c.GetSharedPoolId(), 10, 64)
	if parseErr != nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "invalid shared pool id: %s", c.GetSharedPoolId())
	}

	var (
		operatorId       uint64
		email            string
		openAPIPublicKey string
	)

	reqMeta, err := metadata.ExtractRequestMeta(ctx)
	if err != nil {
		tilog.Warn(ctx, "extract request meta for resource pool instance event", zap.Error(err))
	} else {
		operatorId, err = strconv.ParseUint(reqMeta.UserID, 10, 64)
		if err != nil {
			tilog.Warn(ctx, "parse uint failed", zap.String("id", reqMeta.UserID), zap.Error(err))
		}
		email = reqMeta.Email
		openAPIPublicKey = reqMeta.ApiPublicKey
	}

	instanceId := c.ClusterId
	return &clusterevent.ClusterEvent{
		TenantID:         c.OrganizationId,
		ProjectID:        0,
		ClusterID:        resourcePoolId,
		TraceID:          clusterevent.TraceIDFromContext(ctx),
		OperatorID:       operatorId,
		UserEmail:        email,
		OpenAPIPublicKey: openAPIPublicKey,
		IsDevTier:        false,
		Status:           status,
		EventType:        eventType,
		EventCreatedAtMs: time.Now().UnixMilli(),
		ClusterType:      clusterevent.ClusterTypeTidbxResourcePool,
		ExtraInfo: &clusterevent.EventExtraInfo{
			InstanceID: &instanceId,
		},
		ServicePlan: c.ServicePlan.String(),
	}, nil
}
