// Copyright (c) 2017-2026 Lethean (https://lt.hn)
//
// Licensed under the European Union Public Licence (EUPL) version 1.2.
// You may obtain a copy of the licence at:
//
//     https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
//
// SPDX-License-Identifier: EUPL-1.2

package wallet

import (
	"fmt"

	coreerr "dappco.re/go/core/log"

	"dappco.re/go/core/blockchain/rpc"
	"dappco.re/go/core/blockchain/types"
)

// RingMember is a public key and global index used in ring construction.
type RingMember struct {
	PublicKey   types.PublicKey
	GlobalIndex uint64
}

// RingSelector picks decoy outputs for ring signatures.
type RingSelector interface {
	SelectRing(amount uint64, realGlobalIndex uint64, ringSize int) ([]RingMember, error)
}

// RPCRingSelector fetches decoys from the daemon via RPC.
type RPCRingSelector struct {
	client *rpc.Client
}

// NewRPCRingSelector returns a RingSelector backed by the given RPC client.
func NewRPCRingSelector(client *rpc.Client) *RPCRingSelector {
	return &RPCRingSelector{client: client}
}

// SelectRing fetches random outputs from the daemon and returns ringSize
// decoy members, excluding the real output and any duplicates.
func (s *RPCRingSelector) SelectRing(amount uint64, realGlobalIndex uint64, ringSize int) ([]RingMember, error) {
	outs, err := s.client.GetRandomOutputs(amount, ringSize+5)
	if err != nil {
		return nil, coreerr.E("RPCRingSelector.SelectRing", "wallet: get random outputs", err)
	}

	var members []RingMember
	seen := map[uint64]bool{realGlobalIndex: true}

	for _, out := range outs {
		if seen[out.GlobalIndex] {
			continue
		}
		seen[out.GlobalIndex] = true

		pk, err := types.PublicKeyFromHex(out.PublicKey)
		if err != nil {
			continue
		}
		members = append(members, RingMember{
			PublicKey:   pk,
			GlobalIndex: out.GlobalIndex,
		})
		if len(members) >= ringSize {
			break
		}
	}

	if len(members) < ringSize {
		return nil, coreerr.E("RPCRingSelector.SelectRing", fmt.Sprintf("wallet: insufficient decoys: got %d, need %d", len(members), ringSize), nil)
	}
	return members, nil
}
