package common

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/url"
	"os"
	"strings"

	"github.com/pkg/errors"
)

const (
	ConfigRegionLabelRules = "/pd/api/v1/config/region-label/rules"
	ConfigRegionLabelRule  = "/pd/api/v1/config/region-label/rule/"
)

// Labels is a slice of Label.
type Labels []Label

// Label is used to describe attributes
type Label struct {
	Key   string `json:"key,omitempty"`
	Value string `json:"value,omitempty"`
}

// RegionLabelRules is a slice of RegionRule.
type RegionLabelRules []*RegionLabelRule

// Rule is used to establish the relationship between labels and a key range.
type RegionLabelRule struct {
	ID       string        `json:"id"`
	Index    int           `json:"index"`
	Labels   Labels        `json:"labels"`
	RuleType string        `json:"rule_type"`
	Data     []interface{} `json:"data"`
}

func (r *RegionLabelRules) WriteTo(f *os.File) error {
	regionRuleBytes, err := json.Marshal(r)
	if err != nil {
		return err
	}

	return WriteFile(f, regionRuleBytes)
}

func (r *RegionLabelRules) ReadFrom(f *os.File) error {
	regionRuleBytes, err := io.ReadAll(f)
	if err != nil {
		return err
	}

	return json.Unmarshal(regionRuleBytes, r)
}

const RegionLabelRuleSliceLen = 2

func GetKsIdFromRegionLabel(regionLabelID string) (string, error) {
	// regionLabelID is like "keyspaces/1"
	arr := strings.Split(regionLabelID, "/")
	if len(arr) != RegionLabelRuleSliceLen {
		return "", errors.Errorf("Failed to parse keyspaceId from regionLabelRule:%s", regionLabelID)
	}
	return arr[1], nil
}

func CacheRegionLabelRules(f *os.File) (map[string]*RegionLabelRule, error) {
	var rules RegionLabelRules

	err := rules.ReadFrom(f)
	if err != nil {
		return nil, err
	}
	return RegionLabelRules2Map(rules)
}

func RegionLabelRules2Map(rules RegionLabelRules) (map[string]*RegionLabelRule, error) {
	rulesMap := make(map[string]*RegionLabelRule)
	for _, rule := range rules {
		isKeyspaceRegionLabel := strings.HasPrefix(rule.ID, "keyspaces")
		if isKeyspaceRegionLabel {
			keyspaceId, err := GetKsIdFromRegionLabel(rule.ID)
			if err != nil {
				return nil, err
			}
			rulesMap[keyspaceId] = rule
		}
	}
	return rulesMap, nil
}

func GetAllRegionLabelRules(ctx context.Context, pdctx *PdContext) (RegionLabelRules, error) {
	var rules RegionLabelRules
	res, err := DoRequestForNoNeedConfirm(ctx, pdctx, ConfigRegionLabelRules, "GET", nil, true)
	if err == nil && res != nil {
		err = json.Unmarshal(res, &rules)
	}
	return rules, err
}

func DeleteRegionLabel(ctx context.Context, pdctx *PdContext, regionLabelId string,
	isRun bool, isSkipConfirm bool) error {
	uri := fmt.Sprintf(ConfigRegionLabelRule+"%s", url.QueryEscape(regionLabelId))
	res, err := DoRequestForExecute(ctx, pdctx, uri, "DELETE", nil, isRun, isSkipConfirm)
	if err != nil {
		return err
	}
	if res == nil && isRun {
		return fmt.Errorf("returns error in DeleteRegionLabel")
	}
	return nil
}
