// Package transport provides an abstraction layer for different network transports
package transport

import (
	"fmt"
	"net"
	"sync"
)

// TransportType represents the type of network transport
type TransportType int

const (
	TransportTor TransportType = iota
	TransportMesh
	TransportDirect
)

// String returns string representation of transport type
func (t TransportType) String() string {
	switch t {
	case TransportTor:
		return "Tor"
	case TransportMesh:
		return "Mesh"
	case TransportDirect:
		return "Direct"
	default:
		return "Unknown"
	}
}

// Transport defines the interface for network transports
type Transport interface {
	// Connect establishes a connection to a remote peer
	Connect(address string) (net.Conn, error)

	// Listen starts listening for incoming connections
	Listen(address string) (net.Listener, error)

	// Type returns the transport type
	Type() TransportType

	// Close closes the transport
	Close() error
}

// TransportManager manages multiple transports
type TransportManager struct {
	transports map[TransportType]Transport
	mu         sync.RWMutex
	closed     bool
}

// NewTransportManager creates a new transport manager
func NewTransportManager() *TransportManager {
	return &TransportManager{
		transports: make(map[TransportType]Transport),
	}
}

// RegisterTransport registers a new transport
func (tm *TransportManager) RegisterTransport(transport Transport) error {
	tm.mu.Lock()
	defer tm.mu.Unlock()

	if tm.closed {
		return fmt.Errorf("transport manager is closed")
	}

	transportType := transport.Type()
	if _, exists := tm.transports[transportType]; exists {
		return fmt.Errorf("transport %s already registered", transportType)
	}

	tm.transports[transportType] = transport
	return nil
}

// GetTransport retrieves a transport by type
func (tm *TransportManager) GetTransport(transportType TransportType) (Transport, error) {
	tm.mu.RLock()
	defer tm.mu.RUnlock()

	if tm.closed {
		return nil, fmt.Errorf("transport manager is closed")
	}

	transport, exists := tm.transports[transportType]
	if !exists {
		return nil, fmt.Errorf("transport %s not registered", transportType)
	}

	return transport, nil
}

// Connect connects using the specified transport
func (tm *TransportManager) Connect(transportType TransportType, address string) (net.Conn, error) {
	transport, err := tm.GetTransport(transportType)
	if err != nil {
		return nil, err
	}

	return transport.Connect(address)
}

// Listen starts listening on the specified transport
func (tm *TransportManager) Listen(transportType TransportType, address string) (net.Listener, error) {
	transport, err := tm.GetTransport(transportType)
	if err != nil {
		return nil, err
	}

	return transport.Listen(address)
}

// ListTransports returns all registered transport types
func (tm *TransportManager) ListTransports() []TransportType {
	tm.mu.RLock()
	defer tm.mu.RUnlock()

	types := make([]TransportType, 0, len(tm.transports))
	for transportType := range tm.transports {
		types = append(types, transportType)
	}
	return types
}

// Close closes all transports
func (tm *TransportManager) Close() error {
	tm.mu.Lock()
	defer tm.mu.Unlock()

	if tm.closed {
		return nil
	}

	var lastErr error
	for transportType, transport := range tm.transports {
		if err := transport.Close(); err != nil {
			lastErr = fmt.Errorf("failed to close %s transport: %w", transportType, err)
		}
	}

	tm.transports = make(map[TransportType]Transport)
	tm.closed = true

	return lastErr
}

// Count returns the number of registered transports
func (tm *TransportManager) Count() int {
	tm.mu.RLock()
	defer tm.mu.RUnlock()
	return len(tm.transports)
}

// String returns a string representation
func (tm *TransportManager) String() string {
	tm.mu.RLock()
	defer tm.mu.RUnlock()
	return fmt.Sprintf("TransportManager(transports=%d, closed=%v)", len(tm.transports), tm.closed)
}
