package server

import (
	"fmt"
	"log"
	"net"
	"sync"

	"omnisocketgo/cmd/internal/latencylog"
	"omnisocketgo/cmd/internal/protocol"
	"omnisocketgo/cmd/internal/transport"
)

// UDPOption 用于配置 UDPHub 的可选行为。
type UDPOption func(*UDPHub)

// WithUDPLogger 为 UDP hub 注入时延日志记录器。
func WithUDPLogger(logger latencylog.Logger) UDPOption {
	return func(hub *UDPHub) {
		hub.logger = logger
	}
}

// UDPHub 管理通过 UDP 注册的 peer，并负责在它们之间转发消息。
// 与 TCP Hub 不同，UDPHub 使用单个 net.UDPConn 与所有 peer 通信，
// 通过维护 peerID -> UDPAddr 映射表来寻址。
type UDPHub struct {
	mu    sync.RWMutex
	peers map[string]*net.UDPAddr // peerID -> 对端 UDP 地址
	addrs map[string]string       // addr.String() -> peerID，用于反查

	conn   *transport.UDPConn
	logger latencylog.Logger
}

// NewUDPHub 创建一个新的 UDP 连接中心。
func NewUDPHub(conn *net.UDPConn, opts ...UDPOption) (*UDPHub, error) {
	hub := &UDPHub{
		peers:  make(map[string]*net.UDPAddr),
		addrs:  make(map[string]string),
		logger: latencylog.NoopLogger{},
	}

	for _, opt := range opts {
		opt(hub)
	}

	if hub.logger == nil {
		hub.logger = latencylog.NoopLogger{}
	}

	udpConn, err := transport.NewUDPConn(
		conn,
		nil,
		transport.WithUDPLogger(hub.logger, latencylog.NodeRoleServer, "hub"),
	)
	if err != nil {
		return nil, fmt.Errorf("server: create udp transport conn: %w", err)
	}

	hub.conn = udpConn

	return hub, nil
}

// Serve 启动 UDP 接收主循环，持续读取消息并处理注册/转发。
// 此方法会阻塞，直到底层连接关闭或发生不可恢复的错误。
func (h *UDPHub) Serve() error {
	return h.conn.ReceiveLoop(func(msg protocol.Message, addr *net.UDPAddr) error {
		if err := h.handleMessage(msg, addr); err != nil {
			log.Printf("udp hub: handle message from %s: %v", addr, err)
		}
		return nil // 不因为单条消息处理失败而退出主循环
	})
}

// HasPeer 返回给定 ID 是否已注册到 hub。
func (h *UDPHub) HasPeer(peerID string) bool {
	h.mu.RLock()
	defer h.mu.RUnlock()

	_, ok := h.peers[peerID]
	return ok
}

// handleMessage 处理从指定地址收到的消息。
func (h *UDPHub) handleMessage(msg protocol.Message, addr *net.UDPAddr) error {
	switch msg.Type {
	case protocol.MessageTypeRegister:
		return h.registerPeer(msg, addr)
	case protocol.MessageTypeText, protocol.MessageTypeFile:
		return h.forwardMessage(msg, addr)
	case protocol.MessageTypeError:
		return h.sendErrorTo(addr, msg.From, "peers cannot send error messages")
	default:
		peerID := h.lookupPeerID(addr)
		if peerID == "" {
			peerID = msg.From
		}
		return h.sendErrorTo(addr, peerID, fmt.Sprintf("unsupported message type: %s", msg.Type))
	}
}

// registerPeer 处理 peer 的注册请求。
func (h *UDPHub) registerPeer(msg protocol.Message, addr *net.UDPAddr) error {
	peerID := msg.From
	if peerID == "" {
		return h.sendErrorTo(addr, "", "register: missing peer id")
	}

	h.mu.Lock()
	defer h.mu.Unlock()

	// 如果同一个 peerID 从新地址注册，更新地址映射（支持 peer 重启换端口）。
	if existingAddr, exists := h.peers[peerID]; exists {
		// 清理旧地址的反查映射
		delete(h.addrs, existingAddr.String())
	}

	h.peers[peerID] = addr
	h.addrs[addr.String()] = peerID
	log.Printf("udp hub: registered peer %s from %s", peerID, addr)
	return nil
}

// forwardMessage 转发业务消息到目标 peer。
func (h *UDPHub) forwardMessage(msg protocol.Message, senderAddr *net.UDPAddr) error {
	// 通过来源地址反查发送者 peerID
	senderID := h.lookupPeerID(senderAddr)
	if senderID == "" {
		return h.sendErrorTo(senderAddr, msg.From, "not registered; send register first")
	}

	// server 覆盖 From，不信任客户端自报身份
	msg.From = senderID

	// 查找目标 peer 地址
	targetAddr := h.lookupAddr(msg.To)
	if targetAddr == nil {
		return h.sendErrorTo(senderAddr, senderID, fmt.Sprintf("unknown target: %s", msg.To))
	}

	// 转发消息
	if err := h.conn.SendTo(msg, targetAddr); err != nil {
		// 转发失败，通知发送方
		_ = h.sendErrorTo(senderAddr, senderID, fmt.Sprintf("failed to forward to %s", msg.To))
		return fmt.Errorf("forward to %s at %s: %w", msg.To, targetAddr, err)
	}

	return nil
}

// lookupPeerID 通过 UDP 地址反查 peerID。
func (h *UDPHub) lookupPeerID(addr *net.UDPAddr) string {
	h.mu.RLock()
	defer h.mu.RUnlock()

	return h.addrs[addr.String()]
}

// lookupAddr 通过 peerID 查找 UDP 地址。
func (h *UDPHub) lookupAddr(peerID string) *net.UDPAddr {
	h.mu.RLock()
	defer h.mu.RUnlock()

	return h.peers[peerID]
}

// sendErrorTo 向指定地址发送错误消息。
func (h *UDPHub) sendErrorTo(addr *net.UDPAddr, to, message string) error {
	if to == "" {
		to = "unknown"
	}
	return h.conn.SendTo(protocol.Message{
		Type: protocol.MessageTypeError,
		From: protocol.ServerPeerID,
		To:   to,
		Body: []byte(message),
	}, addr)
}

// Close 关闭底层 UDP 连接。
func (h *UDPHub) Close() error {
	return h.conn.Close()
}
