// Package mesh implements peer-to-peer mesh networking
package mesh

import (
	"fmt"
	"net"
	"sync"
	"time"

	"veilith/transport"
)

// MeshTransport implements peer-to-peer mesh networking
type MeshTransport struct {
	peers      map[string]*Peer // address -> peer
	mu         sync.RWMutex
	listener   net.Listener
	localAddr  string
	closed     bool
	discoveryInterval time.Duration
}

// Peer represents a mesh network peer
type Peer struct {
	Address    string
	PublicKey  string
	LastSeen   time.Time
	Conn       net.Conn
	IsRelay    bool // Can this peer relay messages?
}

// NewMeshTransport creates a new mesh transport
func NewMeshTransport() *MeshTransport {
	return &MeshTransport{
		peers:             make(map[string]*Peer),
		discoveryInterval: 30 * time.Second,
	}
}

// Connect connects to a mesh peer
func (m *MeshTransport) Connect(address string) (net.Conn, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.closed {
		return nil, fmt.Errorf("mesh transport is closed")
	}

	// Check if already connected
	if peer, exists := m.peers[address]; exists {
		if peer.Conn != nil {
			peer.LastSeen = time.Now()
			return peer.Conn, nil
		}
	}

	// Establish new connection
	conn, err := net.DialTimeout("tcp", address, 10*time.Second)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to peer %s: %w", address, err)
	}

	// Add peer
	m.peers[address] = &Peer{
		Address:  address,
		LastSeen: time.Now(),
		Conn:     conn,
	}

	return conn, nil
}

// Listen starts listening for mesh connections
func (m *MeshTransport) Listen(address string) (net.Listener, error) {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.closed {
		return nil, fmt.Errorf("mesh transport is closed")
	}

	if m.listener != nil {
		return nil, fmt.Errorf("already listening on %s", m.localAddr)
	}

	listener, err := net.Listen("tcp", address)
	if err != nil {
		return nil, fmt.Errorf("failed to listen on %s: %w", address, err)
	}

	m.listener = listener
	m.localAddr = address

	// Start peer discovery in background
	go m.startPeerDiscovery()

	return listener, nil
}

// Type returns the transport type
func (m *MeshTransport) Type() transport.TransportType {
	return transport.TransportMesh
}

// Close closes the mesh transport
func (m *MeshTransport) Close() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.closed {
		return nil
	}

	m.closed = true

	// Close listener
	if m.listener != nil {
		if err := m.listener.Close(); err != nil {
			return fmt.Errorf("failed to close listener: %w", err)
		}
		m.listener = nil
	}

	// Close all peer connections
	for addr, peer := range m.peers {
		if peer.Conn != nil {
			peer.Conn.Close()
		}
		delete(m.peers, addr)
	}

	return nil
}

// AddPeer manually adds a peer to the mesh
func (m *MeshTransport) AddPeer(address string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if m.closed {
		return fmt.Errorf("mesh transport is closed")
	}

	if _, exists := m.peers[address]; exists {
		return fmt.Errorf("peer already exists: %s", address)
	}

	m.peers[address] = &Peer{
		Address:  address,
		LastSeen: time.Now(),
	}

	return nil
}

// RemovePeer removes a peer from the mesh
func (m *MeshTransport) RemovePeer(address string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	peer, exists := m.peers[address]
	if !exists {
		return fmt.Errorf("peer not found: %s", address)
	}

	if peer.Conn != nil {
		peer.Conn.Close()
	}

	delete(m.peers, address)
	return nil
}

// GetPeers returns all known peers
func (m *MeshTransport) GetPeers() []*Peer {
	m.mu.RLock()
	defer m.mu.RUnlock()

	peers := make([]*Peer, 0, len(m.peers))
	for _, peer := range m.peers {
		peers = append(peers, peer)
	}
	return peers
}

// GetPeerCount returns the number of known peers
func (m *MeshTransport) GetPeerCount() int {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return len(m.peers)
}

// startPeerDiscovery periodically discovers new peers
func (m *MeshTransport) startPeerDiscovery() {
	ticker := time.NewTicker(m.discoveryInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			m.mu.RLock()
			if m.closed {
				m.mu.RUnlock()
				return
			}
			m.mu.RUnlock()

			// Clean up stale peers
			m.cleanupStalePeers(5 * time.Minute)

			// In a real implementation, you would:
			// 1. Query known peers for their peer lists
			// 2. Attempt connections to new peers
			// 3. Exchange routing tables
		}
	}
}

// cleanupStalePeers removes peers that haven't been seen recently
func (m *MeshTransport) cleanupStalePeers(maxAge time.Duration) {
	m.mu.Lock()
	defer m.mu.Unlock()

	now := time.Now()
	for addr, peer := range m.peers {
		if now.Sub(peer.LastSeen) > maxAge {
			if peer.Conn != nil {
				peer.Conn.Close()
			}
			delete(m.peers, addr)
		}
	}
}

// SetDiscoveryInterval sets the peer discovery interval
func (m *MeshTransport) SetDiscoveryInterval(interval time.Duration) {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.discoveryInterval = interval
}

// String returns a string representation
func (m *MeshTransport) String() string {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return fmt.Sprintf("MeshTransport(peers=%d, listening=%s, closed=%v)",
		len(m.peers), m.localAddr, m.closed)
}
