package core

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

	"git.zkcoi.com/zkcoi/meshray/core/connect"
	"git.zkcoi.com/zkcoi/meshray/core/plugins/wg"
	"git.zkcoi.com/zkcoi/meshray/core/transport"
	"go.uber.org/zap"
)

// Engine 引擎实例 - 一个组网的引擎实例
type Engine struct {
	logger    *zap.Logger
	scheduler *connect.StrategyScheduler
	connMgr   *transport.ConnManager
	relay     *transport.Relay
	plugin    transport.ProtocolPlugin
	metrics   *Metrics

	// 候选地址存储（用于 NotifyPeerInfo）
	candidateStore map[string][]Candidate
	routeIDStore   map[string]uint32
}

// NewEngine 创建引擎实例
func NewEngine(logger *zap.Logger, metrics *Metrics) *Engine {
	// 创建 WG 插件
	plugin := wg.NewWGPlugin()

	// 创建连接管理器
	connMgr := transport.NewConnManager(logger)

	// 创建数据转发器
	relay := transport.NewRelay(plugin, connMgr, logger)

	// 创建策略调度器
	scheduler := connect.NewStrategyScheduler(logger)
	scheduler.OnConnectionUpdate = func(peerID string, conn net.Conn, err error) {
		if err != nil {
			logger.Warn("收到策略调度器连接错误更新", zap.String("peer_id", peerID), zap.Error(err))
		}
		if conn != nil {
			logger.Info("策略调度器连接已建立，开始双向转发", zap.String("peer_id", peerID))
			connMgr.Add(peerID, conn)
			// 启动远端接收协程
			relay.StartReadFromRemoteConn(context.Background(), peerID, conn)
		}
	}

	scheduler.RegisterFactory(connect.NewDirectFactory(nil, logger))                                // 1. Direct-UDP
	scheduler.RegisterFactory(connect.NewFakeTCPFactory(logger))                                    // 2. FakeTCP
	scheduler.RegisterFactory(connect.NewRealTCPFactory(logger))                                    // 3. RealTCP
	scheduler.RegisterFactory(connect.NewTURNFactory(connect.TURNProtocolUDP, nil, "", "", logger)) // 4. TURN-UDP
	// scheduler.RegisterFactory(connect.NewTURNFactoryQUIC(nil, "", "", logger))                      // 5. TURN-QUIC (暂不启用)
	scheduler.RegisterFactory(connect.NewTURNFactory(connect.TURNProtocolTCP, nil, "", "", logger)) // 6. TURN-TCP
	scheduler.RegisterFactory(connect.NewTURNFactory(connect.TURNProtocolTLS, nil, "", "", logger)) // 7. TURN-TLS
	scheduler.RegisterFactory(connect.NewWebRTCFactory(&connect.ICEConfig{}, logger))               // 8. WebRTC
	scheduler.RegisterFactory(connect.NewWSFactory(nil, logger))                                    // 9. WS/WSS

	engine := &Engine{
		logger:         logger,
		scheduler:      scheduler,
		connMgr:        connMgr,
		relay:          relay,
		plugin:         plugin,
		metrics:        metrics,
		candidateStore: make(map[string][]Candidate),
		routeIDStore:   make(map[string]uint32),
	}

	// 设置拨号触发器：当 WG 发包但没连接时自动 9 层拨号
	relay.OnDialTrigger = func(peerKey string) {
		go engine.initiateConnection(peerKey)
	}

	return engine
}

// Start 启动引擎
func (e *Engine) Start() error {
	e.logger.Info("Core 引擎启动")
	return nil
}

// Stop 停止引擎
func (e *Engine) Stop() error {
	e.logger.Info("Core 引擎停止")

	// 关闭所有连接
	if e.connMgr != nil {
		e.connMgr.CloseAll()
	}

	return nil
}

// SetICEConfig 设置 ICE 配置（用于 WebRTC）
func (e *Engine) SetICEConfig(config connect.ICEConfig) error {
	e.logger.Info("更新 ICE 配置",
		zap.Int("stun_servers", len(config.STUNServers)),
		zap.Int("turn_servers", len(config.TURNServers)))

	// TODO: 实现 ICE 配置更新逻辑
	// 1. 找到 WebRTC 工厂
	// 2. 更新其 ICE 配置
	// 3. 重新注册工厂

	// 目前先记录日志，P3 阶段实现
	e.logger.Warn("SetICEConfig 暂未实现，将在 P3 阶段完成")

	return nil
}

// GetScheduler 获取策略调度器
func (e *Engine) GetScheduler() *connect.StrategyScheduler {
	return e.scheduler
}

// GetConnMgr 获取连接管理器
func (e *Engine) GetConnMgr() *transport.ConnManager {
	return e.connMgr
}

// GetRelay 获取数据转发器
func (e *Engine) GetRelay() *transport.Relay {
	return e.relay
}

// GetMetrics 获取监控指标
func (e *Engine) GetMetrics() *Metrics {
	return e.metrics
}

// Bind 为指定 Peer 开启本地端口，开始建连
// peerKey: 对端公钥哈希（8 字符）
// localPort: 本地监听端口（传 0 表示系统自动分配）
// 返回值：实际绑定的端口号
func (e *Engine) Bind(peerKey string, localPort int) (int, error) {
	// 1. 在本地端口监听
	addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: localPort}
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return 0, fmt.Errorf("监听本地端口失败：%w", err)
	}

	actualPort := conn.LocalAddr().(*net.UDPAddr).Port

	// 2. 提取 route_id（从 peerKey 派生）
	routeID := extractRouteID(peerKey)

	// 3. 注册到 Relay
	e.relay.RegisterLocalPort(routeID, conn)

	// 4. 注册到 ConnManager
	e.connMgr.Add(peerKey, nil) // conn 初始为 nil，建连后设置

	// 5. 启动读取协程
	ctx := context.Background()
	e.relay.StartReadFromLocalPort(ctx, routeID, peerKey)

	e.logger.Info("Bind 成功",
		zap.String("peer_key", peerKey),
		zap.Int("local_port", actualPort),
		zap.Uint32("route_id", routeID))

	return actualPort, nil
}

// Unbind 停止指定 Peer 的端口监听
func (e *Engine) Unbind(peerKey string) error {
	// 1. 提取 route_id
	routeID := extractRouteID(peerKey)

	// 2. 从 Relay 注销
	e.relay.UnregisterLocalPort(routeID)

	// 3. 从 ConnManager 移除
	e.connMgr.Remove(peerKey)

	// 4. 清理存储
	delete(e.candidateStore, peerKey)
	delete(e.routeIDStore, peerKey)

	e.logger.Info("Unbind 成功",
		zap.String("peer_key", peerKey),
		zap.Uint32("route_id", routeID))

	return nil
}

// EngineStatus Engine 状态
type EngineStatus struct {
	PeerCount int                    `json:"peer_count"`
	Peers     map[string]*PeerStatus `json:"peers"`

	// Metrics
	ActiveConnections int64  `json:"active_connections"`
	TotalConnections  int64  `json:"total_connections"`
	BytesSent         uint64 `json:"bytes_sent"`
	BytesReceived     uint64 `json:"bytes_received"`
	StrategyFallbacks int64  `json:"strategy_fallbacks"`
	LastSwitchTime    int64  `json:"last_switch_time"` // unix timestamp
}

// PeerStatus Peer 状态
type PeerStatus struct {
	PeerKey   string `json:"peer_key"`
	Connected bool   `json:"connected"`
	Layer     string `json:"layer,omitempty"` // 当前传输层
}

// GetStatus 查询 Engine 状态
func (e *Engine) GetStatus() (*EngineStatus, error) {
	status := &EngineStatus{
		PeerCount: e.connMgr.Count(),
		Peers:     make(map[string]*PeerStatus),
	}

	// 收集 Metrics
	if e.metrics != nil {
		status.ActiveConnections = e.metrics.GetActiveConnections()
		status.TotalConnections = e.metrics.GetTotalConnections()
		status.BytesSent = e.metrics.GetBytesSent()
		status.BytesReceived = e.metrics.GetBytesReceived()
		status.StrategyFallbacks = e.metrics.GetStrategyFallbacks()
		status.LastSwitchTime = e.metrics.GetLastSwitchTime().Unix()
	}

	// 收集所有 Peer 状态
	for peerKey, conn := range e.connMgr.GetAll() {
		peerStatus := &PeerStatus{
			PeerKey:   peerKey,
			Connected: conn != nil,
		}
		if conn != nil {
			peerStatus.Layer = e.scheduler.GetPeerLayer(peerKey).String()
		}
		status.Peers[peerKey] = peerStatus
	}

	return status, nil
}

// Candidate 候选地址（与 connect.Candidate 对齐）
type Candidate struct {
	Addr     string `json:"addr"`     // 候选地址（ip:port）
	Type     string `json:"type"`     // 候选类型：host/srflx/relay
	Priority int    `json:"priority"` // 优先级
	Protocol string `json:"protocol"` // 协议：udp/tcp
}

// NotifyPeerInfo 下发对端候选地址和 route_id
// peerKey: 对端公钥哈希
// candidates: 对端候选地址列表（由信使服务器转发）
// routeID: 路由 ID（用于数据转发）
func (e *Engine) NotifyPeerInfo(peerKey string, candidates []Candidate, routeID uint32) error {
	// 1. 存储候选地址（用于后续建连）
	e.candidateStore[peerKey] = candidates

	// 2. 存储 route_id 映射
	e.routeIDStore[peerKey] = routeID

	// 3. 触发建连流程
	go e.initiateConnection(peerKey)

	e.logger.Info("NotifyPeerInfo 成功",
		zap.String("peer_key", peerKey),
		zap.Int("candidate_count", len(candidates)),
		zap.Uint32("route_id", routeID))

	return nil
}

// initiateConnection 触发建连流程
func (e *Engine) initiateConnection(peerKey string) {
	// 1. 获取候选地址
	candidates := e.candidateStore[peerKey]
	if len(candidates) == 0 {
		return
	}

	// 2. 检查是否已经在拨号或已连接
	if conn, ok := e.connMgr.Get(peerKey); ok && conn != nil {
		return
	}

	// 2. 初始化 DialConfig
	config := &connect.DialConfig{
		PeerID:  peerKey,
		Timeout: 10 * time.Second,
		Logger:  e.logger,
	}

	e.logger.Info("开始建立连接到对端",
		zap.String("peer_key", peerKey),
		zap.Int("candidate_count", len(candidates)))

	// 3. 获取 RouteID
	_, ok := e.routeIDStore[peerKey]
	if !ok {
		e.logger.Warn("未找到 route_id",
			zap.String("peer_key", peerKey))
		return
	}

	// 4. 使用策略调度器尝试建连
	conn, err := e.scheduler.Dial(config)
	if err != nil {
		e.logger.Error("所有策略层尝试连接均失败",
			zap.String("peer_key", peerKey),
			zap.Error(err))
		return
	}

	// 5. 连接成功，更新到 ConnManager
	e.connMgr.Add(peerKey, conn)
	e.logger.Info("连接建立并更新成功", zap.String("peer_key", peerKey))
}

// extractRouteID 从 peerKey 提取 route_id（简化版本）
// 实际应该使用一致的哈希算法
func extractRouteID(peerKey string) uint32 {
	// 简单哈希：取前 4 个字符的 ASCII 码和
	var sum uint32 = 0
	for i := 0; i < len(peerKey) && i < 4; i++ {
		sum += uint32(peerKey[i])
	}
	return sum
}
