package drivers

import (
	"fmt"

	"github.com/tech/sendico/gateway/chain/internal/service/gateway/driver"
	"github.com/tech/sendico/gateway/chain/internal/service/gateway/driver/arbitrum"
	"github.com/tech/sendico/gateway/chain/internal/service/gateway/driver/ethereum"
	"github.com/tech/sendico/gateway/chain/internal/service/gateway/shared"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
	pmodel "github.com/tech/sendico/pkg/model"
	"go.uber.org/zap"
)

// Registry maps configured network keys to chain drivers.
type Registry struct {
	byNetwork map[pmodel.ChainNetwork]driver.Driver
}

// NewRegistry selects drivers for the configured networks.
func NewRegistry(logger mlogger.Logger, networks []shared.Network) (*Registry, error) {
	if logger == nil {
		return nil, merrors.InvalidArgument("driver registry: logger is required")
	}
	result := &Registry{byNetwork: map[pmodel.ChainNetwork]driver.Driver{}}
	for _, network := range networks {
		chainDriver, err := resolveDriver(logger, network.Name)
		if err != nil {
			logger.Error("Unsupported chain driver", zap.String("network", string(network.Name)), zap.Error(err))
			return nil, err
		}
		result.byNetwork[network.Name] = chainDriver
	}
	if len(result.byNetwork) == 0 {
		return nil, merrors.InvalidArgument("driver registry: no supported networks configured")
	}
	logger.Info("Chain drivers configured", zap.Int("count", len(result.byNetwork)))
	return result, nil
}

// Driver resolves a driver for the provided network key.
func (r *Registry) Driver(network pmodel.ChainNetwork) (driver.Driver, error) {
	if r == nil || len(r.byNetwork) == 0 {
		return nil, merrors.Internal("driver registry is not configured")
	}
	chainDriver, ok := r.byNetwork[network]
	if !ok {
		return nil, merrors.InvalidArgument(fmt.Sprintf("unsupported chain network %s", network))
	}
	return chainDriver, nil
}

func resolveDriver(logger mlogger.Logger, network pmodel.ChainNetwork) (driver.Driver, error) {
	switch network {
	case pmodel.ChainNetworkArbitrumOne, pmodel.ChainNetworkArbitrumSepolia:
		return arbitrum.New(logger), nil
	case pmodel.ChainNetworkEthereumMainnet:
		return ethereum.New(logger), nil
	default:
	}
	return nil, merrors.InvalidArgument("unsupported chain network " + string(network))
}
