package vault

import (
	"context"
	"math/big"
	"strings"

	"github.com/ethereum/go-ethereum/core/types"
	"github.com/tech/sendico/pkg/vault/managedkey"
	"go.uber.org/zap"

	"github.com/tech/sendico/gateway/chain/internal/keymanager"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
	pmodel "github.com/tech/sendico/pkg/model"
)

// Config describes how to connect to Vault for managed wallet keys.
type Config = managedkey.Config

// Manager implements the keymanager.Manager contract backed by HashiCorp Vault.
type Manager struct {
	logger mlogger.Logger
	keys   managedkey.Service
}

// New constructs a Vault-backed key manager.
func New(logger mlogger.Logger, cfg Config) (*Manager, error) {
	if logger == nil {
		return nil, merrors.InvalidArgument("vault key manager: logger is required")
	}
	keys, err := managedkey.New(managedkey.Options{
		Logger:           logger,
		Config:           cfg,
		Component:        "vault key manager",
		DefaultKeyPrefix: "gateway/chain/wallets",
	})
	if err != nil {
		logger.Error("Failed to initialise vault key manager", zap.Error(err))
		return nil, err
	}

	return &Manager{
		logger: logger.Named("vault"),
		keys:   keys,
	}, nil
}

// CreateManagedWalletKey creates a new managed wallet key and stores it in Vault.
func (m *Manager) CreateManagedWalletKey(ctx context.Context, walletRef string, network pmodel.ChainNetwork) (*keymanager.ManagedWalletKey, error) {
	if network == pmodel.ChainNetworkUnspecified {
		m.logger.Warn("Network missing for managed key creation", zap.String("wallet_ref", walletRef))
		return nil, merrors.InvalidArgument("vault key manager: network is required")
	}
	networkValue := strings.TrimSpace(string(network))
	if networkValue == "" {
		m.logger.Warn("Network missing for managed key creation", zap.String("wallet_ref", walletRef))
		return nil, merrors.InvalidArgument("vault key manager: network is required")
	}

	created, err := m.keys.CreateManagedWalletKey(ctx, walletRef, networkValue)
	if err != nil {
		m.logger.Warn("Failed to create managed wallet key", zap.String("wallet_ref", walletRef), zap.String("network", networkValue), zap.Error(err))
		return nil, err
	}

	return &keymanager.ManagedWalletKey{
		KeyID:     created.KeyID,
		Address:   created.Address,
		PublicKey: created.PublicKey,
	}, nil
}

// SignTransaction loads the key material from Vault and signs the transaction.
func (m *Manager) SignTransaction(ctx context.Context, keyID string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
	signed, err := m.keys.SignEVMTransaction(ctx, keyID, tx, chainID)
	if err != nil {
		m.logger.Warn("Failed to sign transaction", zap.String("key_id", keyID), zap.Error(err))
		return nil, err
	}
	return signed, nil
}

var _ keymanager.Manager = (*Manager)(nil)
