package resource

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"sort"
	"strings"
)

type ResourceGroupItem struct {
	GroupItemID       int64
	GroupID           int64
	ItemType          string
	ResourceID        int64
	Resource          Resource
	WalletAssetType   string
	WalletAssetAmount int64
	Quantity          int64
	DurationMS        int64
	SortOrder         int32
	CreatedAtMS       int64
	UpdatedAtMS       int64
	// GiftConfigs is populated only for immutable group snapshots. omitempty preserves the canonical JSON
	// of snapshots written before gift semantics became part of the wallet-owned reward promise.
	GiftConfigs []GiftConfig `json:",omitempty"`
}

type ResourceGroup struct {
	AppCode         string
	GroupID         int64
	GroupCode       string
	Name            string
	Status          string
	Description     string
	SortOrder       int32
	Items           []ResourceGroupItem
	CreatedByUserID int64
	UpdatedByUserID int64
	CreatedAtMS     int64
	UpdatedAtMS     int64
}

// ResourceGroupSnapshot is an immutable, owner-controlled reward promise. The embedded group contains
// complete resource and wallet-asset facts needed both for deterministic grant execution and client display.
type ResourceGroupSnapshot struct {
	AppCode                string
	SnapshotID             string
	PinKey                 string
	SourceGroupID          int64
	VersionNo              int64
	SnapshotHash           string
	Group                  ResourceGroup
	CreatedByUserID        int64
	CreatedAtMS            int64
	SourceGroupUpdatedAtMS int64
	SourceContentHash      string
}

type PinResourceGroupSnapshotCommand struct {
	AppCode                   string
	PinKey                    string
	GroupID                   int64
	OperatorUserID            int64
	ExpectedGroupUpdatedAtMS  int64
	ExpectedSourceContentHash string
	RequiredRegionIDs         []int64
	RequiredAllRegions        bool
}

// CanonicalResourceGroupJSON is the owner-side version identity used by both inspect and pin.
// It deliberately includes nested Resource bytes so an independently edited resource cannot reuse a stale pin.
func CanonicalResourceGroupJSON(group ResourceGroup) ([]byte, error) {
	canonical := group
	canonical.Items = append([]ResourceGroupItem(nil), group.Items...)
	for index := range canonical.Items {
		canonical.Items[index].Resource.UsageScopes = append([]string(nil), canonical.Items[index].Resource.UsageScopes...)
		sort.Strings(canonical.Items[index].Resource.UsageScopes)
		canonical.Items[index].GiftConfigs = canonicalGiftConfigs(canonical.Items[index].GiftConfigs)
	}
	sort.SliceStable(canonical.Items, func(left, right int) bool {
		if canonical.Items[left].SortOrder != canonical.Items[right].SortOrder {
			return canonical.Items[left].SortOrder > canonical.Items[right].SortOrder
		}
		return canonical.Items[left].GroupItemID < canonical.Items[right].GroupItemID
	})
	return json.Marshal(canonical)
}

func ResourceGroupContentHash(group ResourceGroup) (string, error) {
	body, err := CanonicalResourceGroupJSON(group)
	if err != nil {
		return "", err
	}
	sum := sha256.Sum256(body)
	return hex.EncodeToString(sum[:]), nil
}

type ResourceGroupItemInput struct {
	ItemType          string
	ResourceID        int64
	WalletAssetType   string
	WalletAssetAmount int64
	Quantity          int64
	DurationMS        int64
	SortOrder         int32
}

type ListResourceGroupsQuery struct {
	AppCode    string
	Status     string
	Keyword    string
	Page       int32
	PageSize   int32
	ActiveOnly bool
}

type ResourceGroupCommand struct {
	AppCode        string
	GroupID        int64
	GroupCode      string
	Name           string
	Status         string
	Description    string
	SortOrder      int32
	Items          []ResourceGroupItemInput
	OperatorUserID int64
}

func ValidGrantStrategy(value string) bool {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case GrantStrategyWalletCredit, GrantStrategyNewEntitlement, GrantStrategyExtendExpiry, GrantStrategyIncreaseQuantity, GrantStrategySetActiveFlag:
		return true
	default:
		return false
	}
}

func NormalizeGroupItemType(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	if value == "" {
		return GroupItemTypeResource
	}
	return value
}

func ValidGroupItemType(value string) bool {
	switch NormalizeGroupItemType(value) {
	case GroupItemTypeResource, GroupItemTypeWalletAsset:
		return true
	default:
		return false
	}
}
