package ctr

import (
	"fmt"
	"net"
	"os/exec"
	"runtime"
	"strings"
	"sync"

	contract "git.zkcoi.com/zkcoi/meshray-contract"
	"go.uber.org/zap"
	"golang.zx2c4.com/wireguard/device"
	"golang.zx2c4.com/wireguard/tun"
	"golang.zx2c4.com/wireguard/wgctrl"
	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)

// WGManager WireGuard 管理器（P2 阶段骨架）
type WGManager struct {
	devices map[string]*WGDevice // network_id -> device
	mu      sync.RWMutex
	logger  *zap.Logger
	wgMode  string // "kernel" | "userspace"（当前运行模式）
}

// WGDevice WireGuard 设备
type WGDevice struct {
	NetworkID string
	Name      string // wg0, wg1, ...
	Config    *DeviceConfig
	Peers     []PeerInfo
	Running   bool
	// 用户态模式下使用的资源引用
	tunDevice tun.Device     // TUN 设备引用（用户态模式）
	wgDevice  *device.Device // WireGuard 设备引用（用户态模式）
}

// NewWGManager 创建 WireGuard 管理器（固定使用用户态）
func NewWGManager(logger *zap.Logger) *WGManager {
	logger.Info("使用 wireguard-go 用户态模式（跨平台统一）")

	return &WGManager{
		devices: make(map[string]*WGDevice),
		logger:  logger,
		wgMode:  "userspace", // 固定为用户态
	}
}

// CreateDevice 创建 WireGuard 设备
// meshMode: "native" 使用 wireguard-go 默认 Bind 直连；"enhanced" 使用 meshray-core 注册的自定义 Bind 接管收发
func (m *WGManager) CreateDevice(networkID string, subnet string, listenPort int, meshMode string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	// 检查是否已存在
	if _, ok := m.devices[networkID]; ok {
		return fmt.Errorf("网络 %s 的设备已存在", networkID)
	}

	deviceName := fmt.Sprintf("wg%s", networkID)

	m.logger.Info("开始创建 WireGuard 设备",
		zap.String("network_id", networkID),
		zap.String("device", deviceName),
		zap.String("subnet", subnet),
		zap.Int("listen_port", listenPort),
		zap.String("mesh_mode", meshMode))

	// 生成密钥对
	privateKey, err := wgtypes.GeneratePrivateKey()
	if err != nil {
		return fmt.Errorf("生成私钥失败：%w", err)
	}
	publicKey := privateKey.PublicKey()

	m.logger.Debug("生成 WG 密钥对",
		zap.String("public_key", publicKey.String()))

	// 使用用户态模式启动 wireguard-go 进程，并获取资源引用
	tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort, networkID, meshMode)
	if err != nil {
		return err
	}

	// 4. ✅ Core 模块使用回环地址拦截，无需配置 TUN 设备 IP
	// WireGuard 用户态设备会自动管理自己的网络接口
	m.logger.Info("WireGuard 用户态设备已创建，使用回环地址拦截模式",
		zap.String("device", deviceName))

	// 5. 启动设备（带回滚）
	if err := m.bringUpDevice(deviceName); err != nil {
		m.cleanupDevice(deviceName) // 回滚
		return fmt.Errorf("启动设备失败：%w", err)
	}

	// 7. 记录到内存
	device := &WGDevice{
		NetworkID: networkID,
		Name:      deviceName,
		Config: &DeviceConfig{
			PrivateKey: privateKey.String(),
			PublicKey:  publicKey.String(),
			ListenPort: listenPort,
			Subnet:     subnet,
		},
		Running: true,
	}

	// ⚠️ 关键：保存用户态模式资源引用
	device.tunDevice = tunDev
	device.wgDevice = wgDev
	m.logger.Debug("已保存用户态模式资源引用",
		zap.String("device", deviceName))

	m.devices[networkID] = device

	m.logger.Info("WireGuard 设备创建成功（用户态模式）",
		zap.String("network_id", networkID),
		zap.String("public_key", publicKey.String()))

	return nil
}

// AddPeer 添加 Peer
func (m *WGManager) AddPeer(networkID string, publicKey, allowedIP string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	device, ok := m.devices[networkID]
	if !ok {
		return fmt.Errorf("网络 %s 的设备不存在", networkID)
	}

	m.logger.Info("添加 Peer",
		zap.String("network_id", networkID),
		zap.String("public_key", truncatePublicKey(publicKey)+"..."),
		zap.String("allowed_ip", allowedIP))

	// 1. 解析公钥
	peerKey, err := wgtypes.ParseKey(publicKey)
	if err != nil {
		return fmt.Errorf("解析公钥失败：%w", err)
	}

	// 2. 解析 AllowedIP
	_, ipNet, err := net.ParseCIDR(allowedIP)
	if err != nil {
		return fmt.Errorf("解析 AllowedIP 失败：%w", err)
	}

	// 3. 连接 wgctrl 并配置
	client, err := wgctrl.New()
	if err != nil {
		return fmt.Errorf("wgctrl 连接失败：%w", err)
	}
	defer client.Close()

	// 4. 配置 Peer
	config := wgtypes.Config{
		Peers: []wgtypes.PeerConfig{
			{
				PublicKey:         peerKey,
				ReplaceAllowedIPs: true,
				AllowedIPs:        []net.IPNet{*ipNet},
			},
		},
	}

	if err := client.ConfigureDevice(device.Name, config); err != nil {
		return fmt.Errorf("配置 Peer 失败：%w", err)
	}

	m.logger.Info("Peer 配置成功",
		zap.String("network_id", networkID),
		zap.String("public_key", truncatePublicKey(publicKey)+"..."),
		zap.String("allowed_ip", allowedIP))

	// 5. 同时更新内存（保持向后兼容）
	peer := PeerInfo{
		PublicKey:  publicKey,
		AllowedIPs: []string{allowedIP},
	}
	device.Peers = append(device.Peers, peer)

	return nil
}

// RemovePeer 移除 Peer
func (m *WGManager) RemovePeer(networkID string, publicKey string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	device, ok := m.devices[networkID]
	if !ok {
		return fmt.Errorf("网络 %s 的设备不存在", networkID)
	}

	m.logger.Info("移除 Peer",
		zap.String("network_id", networkID),
		zap.String("public_key", truncatePublicKey(publicKey)+"..."))

	// 1. 解析公钥
	peerKey, err := wgtypes.ParseKey(publicKey)
	if err != nil {
		return fmt.Errorf("解析公钥失败：%w", err)
	}

	// 2. 连接 wgctrl 并移除
	client, err := wgctrl.New()
	if err != nil {
		return fmt.Errorf("wgctrl 连接失败：%w", err)
	}
	defer client.Close()

	// 3. 配置移除 Peer（使用 Remove: true）
	config := wgtypes.Config{
		Peers: []wgtypes.PeerConfig{
			{
				PublicKey: peerKey,
				Remove:    true,
			},
		},
	}

	if err := client.ConfigureDevice(device.Name, config); err != nil {
		return fmt.Errorf("移除 Peer 失败：%w", err)
	}

	m.logger.Info("Peer 移除成功",
		zap.String("network_id", networkID),
		zap.String("public_key", truncatePublicKey(publicKey)+"..."))

	// 4. 同时从内存删除（保持向后兼容）
	newPeers := []PeerInfo{}
	for _, p := range device.Peers {
		if p.PublicKey != publicKey {
			newPeers = append(newPeers, p)
		}
	}
	device.Peers = newPeers

	return nil
}

// DeleteDevice 删除 WireGuard 设备
func (m *WGManager) DeleteDevice(networkID string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	device, ok := m.devices[networkID]
	if !ok {
		return fmt.Errorf("网络 %s 的设备不存在", networkID)
	}

	m.logger.Info("删除 WireGuard 设备",
		zap.String("network_id", networkID),
		zap.String("device", device.Name))

	// 1. 清理真实设备（内核态或用户态）
	m.cleanupDevice(device.Name)

	// 2. 从内存删除
	delete(m.devices, networkID)

	m.logger.Info("WireGuard 设备已删除",
		zap.String("network_id", networkID))

	return nil
}

// GetStatus 获取设备状态
func (m *WGManager) GetStatus(networkID string) (*WGStatus, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	device, ok := m.devices[networkID]
	if !ok {
		return nil, fmt.Errorf("网络 %s 的设备不存在", networkID)
	}

	status := &WGStatus{
		DeviceName: device.Name,
		Running:    device.Running,
		PeerCount:  len(device.Peers),
		Subnet:     device.Config.Subnet,
	}

	return status, nil
}

// GetWGMode 获取当前 WG 模式
func (m *WGManager) GetWGMode() string {
	m.mu.RLock()
	defer m.mu.RUnlock()
	return m.wgMode
}

// Stop 停止管理器并释放所有资源
func (m *WGManager) Stop() error {
	m.mu.Lock()
	defer m.mu.Unlock()

	m.logger.Info("正在停止 WireGuard 管理器...")

	// 关闭所有设备
	for networkID, device := range m.devices {
		m.logger.Info("正在关闭设备",
			zap.String("network_id", networkID),
			zap.String("device_name", device.Name))

		// 如果是用户态模式，关闭相关资源
		if device.wgDevice != nil {
			m.logger.Debug("关闭用户态 WireGuard 设备",
				zap.String("device", device.Name))
			device.wgDevice.Close()
		}

		if device.tunDevice != nil {
			m.logger.Debug("关闭 TUN 设备",
				zap.String("device", device.Name))
			device.tunDevice.Close()
		}

		// 清理内核态设备（忽略返回值）
		m.cleanupDevice(device.Name)

		delete(m.devices, networkID)
	}

	m.logger.Info("WireGuard 管理器已停止")
	return nil
}

// WGDevice WireGuard 设备（结构已在上面定义）

// DeviceConfig 设备配置
type DeviceConfig struct {
	PrivateKey string
	PublicKey  string
	ListenPort int
	Subnet     string
}

// PeerInfo Peer 信息
type PeerInfo struct {
	PublicKey  string
	Endpoint   string
	AllowedIPs []string
}

// WGStatus WireGuard 状态
type WGStatus struct {
	DeviceName string `json:"device_name"`
	Running    bool   `json:"running"`
	PeerCount  int    `json:"peer_count"`
	Subnet     string `json:"subnet"`
}

// ListPeers 获取设备下所有 Peer 信息
// 除内存记录外，还会从 wgctrl 读取对端真实 Endpoint，作为增强模式 P2P 建连的候选地址来源
func (m *WGManager) ListPeers(networkID string) ([]PeerInfo, error) {
	m.mu.RLock()
	device, ok := m.devices[networkID]
	if !ok {
		m.mu.RUnlock()
		return nil, fmt.Errorf("网络 %s 的设备不存在", networkID)
	}
	// 复制内存中的 peer 列表，避免返回内部共享引用
	peers := make([]PeerInfo, len(device.Peers))
	copy(peers, device.Peers)
	m.mu.RUnlock()

	// 从 wgctrl 读取真实 Endpoint（若已配置）
	client, err := wgctrl.New()
	if err == nil {
		defer client.Close()
		if dev, err := client.Device(device.Name); err == nil {
			for i := range peers {
				for _, wp := range dev.Peers {
					if wp.PublicKey.String() == peers[i].PublicKey {
						if wp.Endpoint != nil {
							peers[i].Endpoint = wp.Endpoint.String()
						}
						break
					}
				}
			}
		}
	}

	return peers, nil
}

// UpdatePeerEndpoint 更新 Peer 的 Endpoint
// 注意：增强模式（meshray-core 接管收发）下不应再改写 peer Endpoint 为回环端口，
// 否则 WG 入站源地址匹配失败；此方法仅用于设置对端真实地址或原生模式。
func (m *WGManager) UpdatePeerEndpoint(networkID string, publicKey string, newEndpoint string) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	device, ok := m.devices[networkID]
	if !ok {
		return fmt.Errorf("网络 %s 的设备不存在", networkID)
	}

	m.logger.Info("更新 Peer Endpoint",
		zap.String("network_id", networkID),
		zap.String("public_key", truncatePublicKey(publicKey)+"..."),
		zap.String("new_endpoint", newEndpoint))

	// 解析公钥
	peerKey, err := wgtypes.ParseKey(publicKey)
	if err != nil {
		return fmt.Errorf("解析公钥失败：%w", err)
	}

	// 解析 Endpoint 地址
	var udpAddr *net.UDPAddr
	if newEndpoint != "" {
		addr, err := net.ResolveUDPAddr("udp", newEndpoint)
		if err != nil {
			return fmt.Errorf("解析 Endpoint 地址 %s 失败：%w", newEndpoint, err)
		}
		udpAddr = addr
	}

	// 连接 wgctrl 并配置
	client, err := wgctrl.New()
	if err != nil {
		return fmt.Errorf("wgctrl 连接失败：%w", err)
	}
	defer client.Close()

	// 配置更新 Peer
	config := wgtypes.Config{
		Peers: []wgtypes.PeerConfig{
			{
				PublicKey:  peerKey,
				UpdateOnly: true,
				Endpoint:   udpAddr,
			},
		},
	}

	if err := client.ConfigureDevice(device.Name, config); err != nil {
		return fmt.Errorf("更新 Peer 失败：%w", err)
	}

	// 同时更新内存
	for i, p := range device.Peers {
		if p.PublicKey == publicKey {
			device.Peers[i].Endpoint = newEndpoint
			break
		}
	}

	return nil
}

// startUserModeWGProcess 用户态模式启动 wireguard-go（保存资源引用）
func (m *WGManager) startUserModeWGProcess(deviceName string, privateKey wgtypes.Key, listenPort int) error {
	tunDev, wgDev, err := m.startUserModeWGProcessWithRefs(deviceName, privateKey, listenPort, "", "native")
	if err != nil {
		return err
	}
	// 兼容旧接口，忽略返回值
	_ = tunDev
	_ = wgDev
	return nil
}

// startUserModeWGProcessWithRefs 用户态模式启动 wireguard-go，返回资源引用
// meshMode 决定 Bind 来源：native 用 wireguard-go 默认 Bind；enhanced 用 meshray-core 注册的自定义 Bind
func (m *WGManager) startUserModeWGProcessWithRefs(deviceName string, privateKey wgtypes.Key, listenPort int, networkID string, meshMode string) (tun.Device, *device.Device, error) {
	m.logger.Info("使用用户态模式启动 WireGuard",
		zap.String("device", deviceName),
		zap.Int("port", listenPort),
		zap.String("mesh_mode", meshMode))

	// 1. 创建 TUN 设备并保存引用
	tunDevice, err := tun.CreateTUN(deviceName, 1420)
	if err != nil {
		return nil, nil, fmt.Errorf("创建 TUN 设备失败：%w", err)
	}

	// 2. 创建 UDP bind：根据组网模式选择
	//    - 原生模式：wireguard-go 默认 Bind（标准直连，无需 meshray-core）
	//    - 增强模式：优先使用 meshray-core 注册的自定义 Bind（接管收发，根治 Endpoint 旁路缺陷）；
	//      若默认构建未引入 meshray-core，则返回错误，增强模式不可用。
	bindFactory, err := contract.GetBindFactory(meshMode == "enhanced")
	if err != nil {
		tunDevice.Close()
		return nil, nil, fmt.Errorf("获取 Bind 工厂失败（增强模式需 meshray-core）：%w", err)
	}
	bind, err := bindFactory(listenPort, networkID, m.logger)
	if err != nil {
		tunDevice.Close()
		return nil, nil, fmt.Errorf("创建 Bind 失败：%w", err)
	}

	// 3. 创建 WireGuard device 并保存引用
	logger := &device.Logger{
		Verbosef: func(format string, args ...interface{}) {
			m.logger.Debug(fmt.Sprintf(format, args...))
		},
		Errorf: func(format string, args ...interface{}) {
			m.logger.Error(fmt.Sprintf(format, args...))
		},
	}

	wgDevice := device.NewDevice(tunDevice, bind, logger)

	// 4. 配置设备
	config := fmt.Sprintf("private_key=%s\nlisten_port=%d\n",
		privateKey.String(), listenPort)

	if err := wgDevice.IpcSet(config); err != nil {
		wgDevice.Close()
		return nil, nil, fmt.Errorf("配置 WireGuard 设备失败：%w", err)
	}

	// 5. 启动设备
	if err := wgDevice.Up(); err != nil {
		wgDevice.Close()
		return nil, nil, fmt.Errorf("启动 WireGuard 设备失败：%w", err)
	}

	m.logger.Info("用户态 WireGuard 启动成功",
		zap.String("device", deviceName),
		zap.Int("port", listenPort))

	// 6. 返回资源引用
	return tunDevice, wgDevice, nil
}

// truncatePublicKey 截断公钥用于日志显示（避免数组越界）
func truncatePublicKey(publicKey string) string {
	if len(publicKey) <= 8 {
		return publicKey
	}
	return publicKey[:8]
}

// bringUpDevice 启动设备（跨平台实现）
func (m *WGManager) bringUpDevice(deviceName string) error {
	m.logger.Info("启动设备",
		zap.String("device", deviceName))

	var cmd *exec.Cmd
	var output []byte
	var err error

	switch runtime.GOOS {
	case "linux":
		// Linux: ip link set up device
		cmd = exec.Command("ip", "link", "set", "up", deviceName)
		output, err = cmd.CombinedOutput()
	case "windows":
		// Windows: 用户态模式下 wireguard-go 会自动管理设备状态
		// 这里不需要额外操作
		m.logger.Debug("Windows 平台用户态模式无需手动启动设备")
		return nil
	case "darwin":
		// macOS: ifconfig device up
		cmd = exec.Command("ifconfig", deviceName, "up")
		output, err = cmd.CombinedOutput()
	default:
		return fmt.Errorf("不支持的操作系统：%s", runtime.GOOS)
	}

	if err != nil {
		return fmt.Errorf("启动设备失败 (%s): %w", string(output), err)
	}

	m.logger.Info("设备已启动",
		zap.String("device", deviceName))

	return nil
}

// cleanupDevice 清理残留设备（回滚用，跨平台实现）
func (m *WGManager) cleanupDevice(deviceName string) {
	m.logger.Warn("清理残留设备",
		zap.String("device", deviceName))

	var cmd *exec.Cmd
	var output []byte

	switch runtime.GOOS {
	case "linux":
		// Linux: ip link delete device
		cmd = exec.Command("ip", "link", "delete", deviceName)
		output, _ = cmd.CombinedOutput()
	case "windows":
		// Windows: 无法通过命令行直接删除，需要用户手动操作
		m.logger.Warn("Windows 平台需要通过 WireGuard 客户端删除设备",
			zap.String("device", deviceName))
		return
	case "darwin":
		// macOS: ifconfig device down delete
		cmd = exec.Command("ifconfig", deviceName, "down")
		_, _ = cmd.CombinedOutput()
		// macOS 可能需要额外步骤，这里简化处理
	default:
		m.logger.Warn("不支持的操作系统，跳过清理",
			zap.String("os", runtime.GOOS))
		return
	}

	// 检查是否是"设备不存在"错误
	if strings.Contains(string(output), "does not exist") ||
		strings.Contains(string(output), "cannot find device") {
		m.logger.Debug("设备不存在，无需清理",
			zap.String("device", deviceName))
		return
	}

	m.logger.Debug("设备清理完成",
		zap.String("device", deviceName))
}
