package utils

import (
	"errors"
	"fmt"
	"strconv"
	"strings"

	"golang.org/x/exp/constraints"
)

type IntRanges[T constraints.Integer] []Range[T]

var errIntRanges = errors.New("intRanges error")

func NewIntRanges[T constraints.Integer](expected string) (IntRanges[T], error) {
	// example: 200 or 200/302 or 200-400 or 200/204/401-429/501-503
	expected = strings.TrimSpace(expected)
	if len(expected) == 0 || expected == "*" {
		return nil, nil
	}

	list := strings.Split(expected, "/")
	if len(list) > 28 {
		return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges)
	}

	return NewIntRangesFromList[T](list)
}

func NewIntRangesFromList[T constraints.Integer](list []string) (IntRanges[T], error) {
	var ranges IntRanges[T]
	for _, s := range list {
		if s == "" {
			continue
		}

		status := strings.Split(s, "-")
		statusLen := len(status)
		if statusLen > 2 {
			return nil, errIntRanges
		}

		start, err := strconv.ParseInt(strings.Trim(status[0], "[ ]"), 10, 64)
		if err != nil {
			return nil, errIntRanges
		}

		switch statusLen {
		case 1:
			ranges = append(ranges, NewRange(T(start), T(start)))
		case 2:
			end, err := strconv.ParseUint(strings.Trim(status[1], "[ ]"), 10, 64)
			if err != nil {
				return nil, errIntRanges
			}

			ranges = append(ranges, NewRange(T(start), T(end)))
		}
	}

	return ranges, nil
}

func (ranges IntRanges[T]) Check(status T) bool {
	if len(ranges) == 0 {
		return true
	}

	for _, segment := range ranges {
		if segment.Contains(status) {
			return true
		}
	}

	return false
}
