package connect

import (
	"context"
	"fmt"
	"net"
	"time"

	"go.uber.org/zap"
)

// DirectFactory Direct-UDP 直连工厂（Layer 1）
type DirectFactory struct {
	stunServers []string
	logger      *zap.Logger
}

// NewDirectFactory 创建 Direct-UDP 工厂
func NewDirectFactory(stunServers []string, logger *zap.Logger) *DirectFactory {
	return &DirectFactory{
		stunServers: stunServers,
		logger:      logger,
	}
}

// Layer 返回传输层类型
func (f *DirectFactory) Layer() Layer {
	return LayerDirectUDP
}

// Name 返回传输方式名称
func (f *DirectFactory) Name() string {
	return "Direct-UDP"
}

// Dial 建立 Direct-UDP 直连
func (f *DirectFactory) Dial(ctx context.Context, config *DialConfig) (net.Conn, error) {
	f.logger.Info("开始建立 Direct-UDP 直连",
		zap.String("peer_id", config.PeerID))

	servers := f.stunServers
	if len(servers) == 0 {
		servers = config.STUNServers
	}

	if len(servers) == 0 {
		f.logger.Warn("未配置 STUN 服务器列表，仅尝试内部 P2P 打洞")
	}

	var candidates []string
	if len(servers) > 0 {
		// 1. 创建 STUN 客户端收集候选地址
		stun := NewSTUNClient(servers, f.logger)
		candidates = stun.CollectCandidates()
	}

	if len(candidates) == 0 {
		f.logger.Warn("未能收集到任何 STUN 候选地址，回退至 PeerID")
		// As a fallback, maybe PeerID contains IP:PORT
		candidates = append(candidates, config.PeerID)
	}

	f.logger.Info("STUN 候选地址收集完成",
		zap.Strings("candidates", candidates))

	// 2. 实际 P2P 连接尝试
	f.logger.Warn("当前尝试所有候选地址...")

	dialer := &net.Dialer{
		Timeout: 5 * time.Second,
	}

	// 建立 UDP 连接并返回最近可用的
	var lastErr error
	for _, candidate := range candidates {
		if candidate == "" { continue }
		
		conn, err := dialer.DialContext(ctx, "udp", candidate)
		if err == nil {
			f.logger.Info("Direct-UDP 直连建立成功",
				zap.String("peer_id", config.PeerID),
				zap.String("remote_addr", conn.RemoteAddr().String()))
			return conn, nil
		}
		
		f.logger.Warn("候选地址连接失败",
			zap.String("candidate", candidate),
			zap.Error(err))
		lastErr = err
	}

	return nil, fmt.Errorf("所有候选地址 UDP 连接均失败，最后错误：%w", lastErr)
}
