// Copyright (c) 2017-2026 Lethean (https://lt.hn)
//
// Licensed under the European Union Public Licence (EUPL) version 1.2.
// SPDX-License-Identifier: EUPL-1.2

package wire

import (
	"bytes"
	"fmt"

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

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

// VariantElement is one tagged element from a raw variant vector.
// Data contains the raw wire bytes for the element payload, without the tag.
type VariantElement struct {
	Tag  uint8
	Data []byte
}

// DecodeVariantVector decodes a raw variant vector into tagged raw elements.
// It is useful for higher-level validation of raw transaction fields such as
// extra, attachment, signatures, and proofs.
func DecodeVariantVector(raw []byte) ([]VariantElement, error) {
	dec := NewDecoder(bytes.NewReader(raw))
	count := dec.ReadVarint()
	if dec.Err() != nil {
		return nil, dec.Err()
	}

	elems := make([]VariantElement, 0, int(count))
	for i := uint64(0); i < count; i++ {
		tag := dec.ReadUint8()
		if dec.Err() != nil {
			return nil, coreerr.E("DecodeVariantVector", fmt.Sprintf("read tag %d", i), dec.Err())
		}

		data := readVariantElementData(dec, tag)
		if dec.Err() != nil {
			return nil, coreerr.E("DecodeVariantVector", fmt.Sprintf("read element %d", i), dec.Err())
		}

		elems = append(elems, VariantElement{Tag: tag, Data: data})
	}

	return elems, nil
}

// DecodeAssetDescriptorOperation decodes a raw asset_descriptor_operation
// payload into its typed representation.
func DecodeAssetDescriptorOperation(raw []byte) (types.AssetDescriptorOperation, error) {
	dec := NewDecoder(bytes.NewReader(raw))
	var op types.AssetDescriptorOperation

	op.Version = dec.ReadUint8()
	op.OperationType = dec.ReadUint8()

	if dec.ReadUint8() != 0 {
		assetID := dec.ReadBytes(32)
		if dec.Err() != nil {
			return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read asset id", dec.Err())
		}
		copy(op.AssetID[:], assetID)
	}

	if dec.ReadUint8() != 0 {
		desc := &types.AssetDescriptorBase{}
		desc.Ticker = decodeStringField(dec)
		desc.FullName = decodeStringField(dec)
		desc.TotalMaxSupply = dec.ReadUint64LE()
		desc.CurrentSupply = dec.ReadUint64LE()
		desc.DecimalPoint = dec.ReadUint8()
		desc.MetaInfo = decodeStringField(dec)
		ownerKey := dec.ReadBytes(32)
		if dec.Err() != nil {
			return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read owner key", dec.Err())
		}
		copy(desc.OwnerKey[:], ownerKey)
		desc.Etc = readVariantVectorFixed(dec, 1)
		if dec.Err() != nil {
			return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read descriptor etc", dec.Err())
		}
		op.Descriptor = desc
	}

	op.AmountToEmit = dec.ReadUint64LE()
	op.AmountToBurn = dec.ReadUint64LE()
	op.Etc = readVariantVectorFixed(dec, 1)
	if dec.Err() != nil {
		return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "read trailing etc", dec.Err())
	}

	if dec.Err() != nil {
		return types.AssetDescriptorOperation{}, coreerr.E("DecodeAssetDescriptorOperation", "decode asset descriptor operation", dec.Err())
	}

	return op, nil
}
