package auth

import (
	"context"
	"fmt"
	"net/netip"

	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
)

type IPValidator interface {
	ValidateIP(ctx context.Context, ip string, cidrs []string) (bool, string)
}

type ipValidator struct {
	prefixCache *container.Map[string, netip.Prefix] // key: cidr, value: set of matched IPs
}

func NewIPValidator() IPValidator {
	return &ipValidator{
		prefixCache: container.NewMap[string, netip.Prefix](),
	}
}

func (v *ipValidator) ValidateIP(ctx context.Context, ip string, cidrs []string) (bool, string) {
	if ip == "" {
		return false, "ip is empty"
	}

	addr, err := netip.ParseAddr(ip)
	if err != nil {
		return false, fmt.Sprintf("parse ip %s failed, ip", ip)
	}

	for _, cidr := range cidrs {
		if cidr == "" {
			continue
		}

		prefix, ok := v.prefixCache.Load(cidr)
		if !ok {
			p, err := netip.ParsePrefix(cidr)
			if err != nil {
				log.FromContext(ctx).Info("skip invalid cidr", "cidr", "cidr", "error", err)
				continue
			}
			prefix = p

			v.prefixCache.Store(cidr, prefix)
		}

		if prefix.Contains(addr) {
			return true, ""
		}
	}

	return false, fmt.Sprintf("ip %s not allowed", ip)
}
