package tronclient

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/tech/sendico/gateway/tron/shared"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
	"go.uber.org/zap"
)

// Registry manages TRON gRPC clients keyed by network name.
type Registry struct {
	logger  mlogger.Logger
	clients map[string]*Client
}

// NewRegistry creates an empty registry.
func NewRegistry(logger mlogger.Logger) *Registry {
	return &Registry{
		logger:  logger.Named("tron_registry"),
		clients: make(map[string]*Client),
	}
}

// Prepare initializes TRON gRPC clients for all networks with a configured GRPCUrl.
// Networks without GRPCUrl are skipped (they will fallback to EVM).
func Prepare(ctx context.Context, logger mlogger.Logger, networks []shared.Network) (*Registry, error) {
	if logger == nil {
		return nil, merrors.InvalidArgument("tronclient: logger is required")
	}

	registry := NewRegistry(logger)
	timeout := 30 * time.Second

	for _, network := range networks {
		name := network.Name.String()
		grpcURL := strings.TrimSpace(network.GRPCUrl)
		grpcToken := strings.TrimSpace(network.GRPCToken)
		forceIPv4 := network.GRPCForceIPv4

		if !network.Name.IsValid() {
			continue
		}

		// Skip networks without TRON gRPC URL configured
		if grpcURL == "" {
			registry.logger.Debug("Skipping network without TRON gRPC URL",
				zap.String("network", name),
			)
			continue
		}

		registry.logger.Info("Initializing TRON gRPC client",
			zap.String("network", name),
			zap.String("grpc_url", grpcURL),
			zap.Bool("force_ipv4", forceIPv4),
		)

		client, err := NewClient(grpcURL, timeout, grpcToken, forceIPv4)
		if err != nil {
			registry.Close()
			registry.logger.Error("Failed to initialize TRON gRPC client",
				zap.String("network", name),
				zap.Error(err),
			)
			return nil, merrors.Internal(fmt.Sprintf("tronclient: failed to connect to %s: %v", name, err))
		}

		registry.clients[name] = client
		registry.logger.Info("TRON gRPC client ready",
			zap.String("network", name),
		)
	}

	if len(registry.clients) > 0 {
		registry.logger.Info("TRON gRPC clients initialized",
			zap.Int("count", len(registry.clients)),
		)
	} else {
		registry.logger.Debug("No TRON gRPC clients were initialized (no networks with grpc_url_env)")
	}

	return registry, nil
}

// Client returns the TRON gRPC client for the given network.
func (r *Registry) Client(networkName string) (*Client, error) {
	if r == nil {
		return nil, merrors.Internal("tronclient: registry not initialized")
	}

	name := strings.ToLower(strings.TrimSpace(networkName))
	client, ok := r.clients[name]
	if !ok || client == nil {
		return nil, merrors.InvalidArgument(fmt.Sprintf("tronclient: no client for network %s", name))
	}

	return client, nil
}

// HasClient checks if a TRON gRPC client is available for the given network.
func (r *Registry) HasClient(networkName string) bool {
	if r == nil || len(r.clients) == 0 {
		return false
	}
	name := strings.ToLower(strings.TrimSpace(networkName))
	client, ok := r.clients[name]
	return ok && client != nil
}

// Close closes all TRON gRPC connections.
func (r *Registry) Close() {
	if r == nil {
		return
	}
	for name, client := range r.clients {
		if client != nil {
			client.Close()
			if r.logger != nil {
				r.logger.Info("TRON gRPC client closed", zap.String("network", name))
			}
		}
	}
	r.clients = make(map[string]*Client)
}
