package shared

import (
	"strings"

	"github.com/tech/sendico/gateway/chain/storage/model"
	chainasset "github.com/tech/sendico/pkg/chain"
	moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
	chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

// CloneMoney defensively copies a Money proto.
func CloneMoney(m *moneyv1.Money) *moneyv1.Money {
	if m == nil {
		return nil
	}
	return &moneyv1.Money{Amount: m.GetAmount(), Currency: m.GetCurrency()}
}

// CloneMetadata defensively copies metadata maps.
func CloneMetadata(input map[string]string) map[string]string {
	if len(input) == 0 {
		return nil
	}
	clone := make(map[string]string, len(input))
	for k, v := range input {
		clone[k] = v
	}
	return clone
}

// ResolveContractAddress finds a token contract for a symbol in a case-insensitive manner.
func ResolveContractAddress(tokens []TokenContract, symbol string) string {
	upper := strings.ToUpper(symbol)
	for _, token := range tokens {
		if strings.EqualFold(token.Symbol, upper) && token.ContractAddress != "" {
			return strings.ToLower(token.ContractAddress)
		}
	}
	return ""
}

func GenerateWalletRef() string {
	return primitive.NewObjectID().Hex()
}

func GenerateTransferRef() string {
	return primitive.NewObjectID().Hex()
}

func ChainKeyFromEnum(chain chainv1.ChainNetwork) (string, chainv1.ChainNetwork) {
	if name, ok := chainv1.ChainNetwork_name[int32(chain)]; ok {
		key := strings.ToLower(strings.TrimPrefix(name, "CHAIN_NETWORK_"))
		return key, chain
	}
	return "", chainv1.ChainNetwork_CHAIN_NETWORK_UNSPECIFIED
}

func ChainEnumFromName(name string) chainv1.ChainNetwork {
	return chainasset.NetworkFromString(name)
}

func ManagedWalletStatusToProto(status model.ManagedWalletStatus) chainv1.ManagedWalletStatus {
	switch status {
	case model.ManagedWalletStatusActive:
		return chainv1.ManagedWalletStatus_MANAGED_WALLET_ACTIVE
	case model.ManagedWalletStatusSuspended:
		return chainv1.ManagedWalletStatus_MANAGED_WALLET_SUSPENDED
	case model.ManagedWalletStatusClosed:
		return chainv1.ManagedWalletStatus_MANAGED_WALLET_CLOSED
	default:
		return chainv1.ManagedWalletStatus_MANAGED_WALLET_STATUS_UNSPECIFIED
	}
}

func TransferStatusToModel(status chainv1.TransferStatus) model.TransferStatus {
	switch status {
	case chainv1.TransferStatus_TRANSFER_PENDING:
		return model.TransferStatusPending
	case chainv1.TransferStatus_TRANSFER_SIGNING:
		return model.TransferStatusSigning
	case chainv1.TransferStatus_TRANSFER_SUBMITTED:
		return model.TransferStatusSubmitted
	case chainv1.TransferStatus_TRANSFER_CONFIRMED:
		return model.TransferStatusConfirmed
	case chainv1.TransferStatus_TRANSFER_FAILED:
		return model.TransferStatusFailed
	case chainv1.TransferStatus_TRANSFER_CANCELLED:
		return model.TransferStatusCancelled
	default:
		return ""
	}
}

func TransferStatusToProto(status model.TransferStatus) chainv1.TransferStatus {
	switch status {
	case model.TransferStatusPending:
		return chainv1.TransferStatus_TRANSFER_PENDING
	case model.TransferStatusSigning:
		return chainv1.TransferStatus_TRANSFER_SIGNING
	case model.TransferStatusSubmitted:
		return chainv1.TransferStatus_TRANSFER_SUBMITTED
	case model.TransferStatusConfirmed:
		return chainv1.TransferStatus_TRANSFER_CONFIRMED
	case model.TransferStatusFailed:
		return chainv1.TransferStatus_TRANSFER_FAILED
	case model.TransferStatusCancelled:
		return chainv1.TransferStatus_TRANSFER_CANCELLED
	default:
		return chainv1.TransferStatus_TRANSFER_STATUS_UNSPECIFIED
	}
}

// NativeCurrency returns the canonical native token symbol for a network.
func NativeCurrency(network Network) string {
	currency := strings.ToUpper(strings.TrimSpace(network.NativeToken))
	if currency == "" {
		currency = strings.ToUpper(strings.TrimSpace(network.Name))
	}
	return currency
}

// Network describes a supported blockchain network and known token contracts.
type Network struct {
	Name           string
	RPCURL         string
	ChainID        uint64
	NativeToken    string
	TokenConfigs   []TokenContract
	GasTopUpPolicy *GasTopUpPolicy
}

// TokenContract captures the metadata needed to work with a specific on-chain token.
type TokenContract struct {
	Symbol          string
	ContractAddress string
}

// ServiceWallet captures the managed service wallet configuration.
type ServiceWallet struct {
	Network    string
	Address    string
	PrivateKey string
}
