package metadata

import (
	"context"
	"errors"
	"strconv"

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

type ExpectRequest[T any] struct {
	TenantId   T
	ProjectId  T
	ClusterId  T
	ResourceId string
}

func (a *ExpectRequest[T]) equal(o *RequestMeta) bool {
	if !compare(a.TenantId, o.OrgID) {
		return false
	}

	if !compare(a.ProjectId, o.DefaultProjectID) {
		return false
	}

	if !compare(a.ClusterId, o.ClusterID) {
		return false
	}

	if a.ResourceId != "" && o.ResourceID != "" && a.ResourceId != o.ResourceID {
		return false
	}

	return true
}

func compare[T any](a T, b uint64) bool {
	if b == 0 {
		return true
	}

	switch v := any(a).(type) {
	case uint64, uint:
		if v == uint64(0) || v == uint(0) {
			return true
		}

		return v == b
	case string:
		if v == "" {
			return true
		}
		return v == strconv.FormatUint(b, 10)
	default:
		return false
	}
}

func Validate[T any](ctx context.Context, expect *ExpectRequest[T]) error {
	meta, err := extractRequestMeta(ctx, noErrorExtractor)
	if err != nil {
		return err
	}

	if meta == nil {
		tilog.Error(ctx, "extract metadata failed")
		return errors.New("extract metadata failed")
	}

	err = lo.Validate(expect.equal(meta), "validation failed")
	if err != nil {
		tilog.Error(ctx, "validation failed", zap.Any("expect", lo.FromPtr(expect)), zap.Any("meta", lo.FromPtr(meta)))
	}

	return err
}
