package main

import (
	"encoding/hex"
	"fmt"
	"regexp"
	"strings"
	"time"

	"tinygo.org/x/bluetooth"
)

// --- 替换为你的设备的 UUID ---
var (
	// 通知/指示通道
	charNotify = "0000ff01-f7e3-55b4-6c4c-9fd140100a16"
	// 写入通道
	charWrite = "0000ff02-f7e3-55b4-6c4c-9fd140100a16"
	// 可读特征值
	charRead = "0000ff53-0000-1000-8000-00805f9b34fb"
)

var adapter = bluetooth.DefaultAdapter
var stopChan = make(chan bool, 1) // 增加缓冲区，防止阻塞扫描回调
var targetName = ""               // 扫描时用于过滤的可选设备名（例如填写 "Meter"）

// 全局标志，表示是否已找到目标设备
var foundTarget = false

func main() {
	// 1. 扫描设备
	fmt.Println("开始扫描蓝牙设备...")
	if err := adapter.Enable(); err != nil {
		panic(fmt.Sprintf("开启适配器失败: %v", err))
	}

	fmt.Println("正在扫描中，请确保设备处于可发现状态 (10秒)...")

	// 存储扫描发现的设备对象列表
	var foundDevices []bluetooth.ScanResult

	go func() {
		time.Sleep(10 * time.Second)
		if !foundTarget {
			fmt.Println("\n扫描超时，正在自动停止扫描...")
			_ = adapter.StopScan() // 触发底层停止，让主线程的 adapter.Scan() 退出阻塞
			select {
			case stopChan <- true:
			default:
			}
		}
	}()

	// 开始扫描
	err := adapter.Scan(func(adapter *bluetooth.Adapter, device bluetooth.ScanResult) {
		name := device.LocalName()
		if name == "" {
			name = "[未知设备]"
		}

		// 避免内存列表中重复添加同一设备
		isDup := false
		for _, d := range foundDevices {
			if d.Address.String() == device.Address.String() {
				isDup = true
				break
			}
		}
		if !isDup {
			foundDevices = append(foundDevices, device)
			fmt.Printf("发现新设备: %s (%s), RSSI: %d\n", device.Address.String(), name, device.RSSI)
		}

		// 如果设置了目标设备名称，且匹配，则停止扫描
		if targetName != "" && strings.Contains(strings.ToLower(name), strings.ToLower(targetName)) {
			fmt.Printf("\n找到目标设备: %s (%s)\n", device.Address.String(), name)
			foundTarget = true
			_ = adapter.StopScan()
			select {
			case stopChan <- true:
			default:
			}
		}
	})
	if err != nil {
		panic(fmt.Sprintf("扫描失败: %v", err))
	}

	// 等待扫描结束（10秒超时或通过 stopChan 主动停止）
	select {
	case <-stopChan:
		fmt.Println("扫描已停止。")
	case <-time.After(10 * time.Second):
		fmt.Println("扫描超时，停止扫描。")
		_ = adapter.StopScan()
	}

	// 决定连接哪一个设备地址
	var targetAddress bluetooth.Address
	var addrSelected = false

	if foundTarget && len(foundDevices) > 0 {
		// 如果根据 targetName 锁定了设备，使用最后一次追加的设备
		targetAddress = foundDevices[len(foundDevices)-1].Address
		addrSelected = true
		fmt.Printf("将使用扫描发现的自动匹配设备: %s\n", targetAddress.String())
	}

	// 如果没有设置 targetName 或未匹配到，则显示列表供用户手动选择
	if !addrSelected {
		if len(foundDevices) == 0 {
			fmt.Println("\n未扫描到任何设备！")
			fmt.Print("请输入要连接的设备 MAC 地址（例如 11:22:33:AA:BB:CC）: ")
			var inputStr string
			fmt.Scanln(&inputStr)
			inputStr = strings.TrimSpace(inputStr)
			if inputStr == "" {
				fmt.Println("地址不能为空!!!")
				return
			}
			targetAddress = parseAddressFromString(inputStr)
		} else {
			fmt.Println("\n====== 扫描到的设备列表 ======")
			for i, dev := range foundDevices {
				name := dev.LocalName()
				if name == "" {
					name = "[未知名称]"
				}
				fmt.Printf("[%d]: %s (%s)\n", i+1, dev.Address.String(), name)
			}
			fmt.Print("\n请输入要连接的设备序号或直接输入MAC地址: ")
			var input string
			fmt.Scanln(&input)
			input = strings.TrimSpace(input)

			var index int
			if _, err := fmt.Sscan(input, &index); err == nil && index > 0 && index <= len(foundDevices) {
				// 用户输入的是数字序号
				targetAddress = foundDevices[index-1].Address
			} else if input != "" {
				// 用户直接输入了 MAC 地址字符串
				targetAddress = parseAddressFromString(input)
			} else {
				fmt.Println("输入无效，程序退出。")
				return
			}
		}
	}

	// 2. 连接设备并进行原定逻辑交互
	if err := connectAndInteract(targetAddress); err != nil {
		fmt.Printf("\n[错误] 交互中途失败: %v\n", err)
	}
}

func connectAndInteract(address bluetooth.Address) error {
	fmt.Printf("\n正在建立连接 %s ...\n", address.String())
	device, err := adapter.Connect(address, bluetooth.ConnectionParams{})
	if err != nil {
		return fmt.Errorf("连接失败: %v", err)
	}
	defer func() {
		fmt.Println("执行断开连接!")
		_ = device.Disconnect()
	}()

	fmt.Println("蓝牙设备连接成功!")
	time.Sleep(500 * time.Millisecond) // 给硬件反应时间

	// 3. 发现服务并查找特征值
	fmt.Println("\n正在发现服务...")
	services, err := device.DiscoverServices(nil)
	if err != nil {
		return fmt.Errorf("发现服务失败: %v", err)
	}

	// 正确使用远程特征值结构体类型
	var targetWriteChar bluetooth.DeviceCharacteristic
	var targetNotifyChar bluetooth.DeviceCharacteristic
	var targetReadChar bluetooth.DeviceCharacteristic

	var hasWrite, hasNotify, hasRead bool

	// 解析所有服务的特征值 UUID
	for _, service := range services {
		fmt.Printf("发现服务: %s\n", service.UUID().String())
		chars, err := service.DiscoverCharacteristics(nil)
		if err != nil {
			fmt.Printf("  发现特征失败: %v\n", err)
			continue
		}
		for i := range chars {
			uuidStr := strings.ToLower(chars[i].UUID().String())
			fmt.Printf("  发现特征: %s\n", uuidStr)

			if uuidStr == strings.ToLower(charWrite) {
				targetWriteChar = chars[i]
				hasWrite = true
			} else if uuidStr == strings.ToLower(charNotify) {
				targetNotifyChar = chars[i]
				hasNotify = true
			} else if uuidStr == strings.ToLower(charRead) {
				targetReadChar = chars[i]
				hasRead = true
			}
		}
	}

	if !hasWrite {
		return fmt.Errorf("未找到写入特征值 %s", charWrite)
	}
	if !hasNotify {
		return fmt.Errorf("未找到通知特征值 %s", charNotify)
	}

	// 4. 激活通知接收通道（完全保留你原来的 channel 异步接收设计）
	fmt.Println("\n正在启用通知监听...")
	notifyChan := make(chan []byte, 10)

	err = targetNotifyChar.EnableNotifications(func(data []byte) {
		copiedData := make([]byte, len(data))
		copy(copiedData, data)

		fmt.Printf("\n[收到底层通知] Hex: %s\n", hex.EncodeToString(copiedData))
		select {
		case notifyChan <- copiedData:
		default:
		}
	})
	if err != nil {
		return fmt.Errorf("启用通知失败: %v", err)
	}

	// 程序退出该函数时关闭通知监听（新版使用传入 nil 替代旧的 Stop 方法）
	defer func() {
		_ = targetNotifyChar.EnableNotifications(nil)
	}()

	// 5. 发送指令 (0x11) 并接收响应
	fmt.Println("\n读取版本信息(0x11)...")
	_, err = targetWriteChar.Write([]byte{0x11})
	if err != nil {
		return fmt.Errorf("指令发送失败: %v", err)
	}
	fmt.Println("指令已发送，等待响应...")

	// 6. 完美对齐你的 3 秒超时等待接收逻辑
	select {
	case resp := <-notifyChan:
		fmt.Printf("收到响应 Hex: %s | 字符串: %s\n", hex.EncodeToString(resp), string(resp))
	case <-time.After(3 * time.Second):
		fmt.Println("等待响应超时。")
	}

	// 7. (可选) 读取其他可读特征值
	if hasRead {
		fmt.Println("\n读取可读特征值...")
		buf := make([]byte, 64)
		n, err := targetReadChar.Read(buf)
		if err != nil {
			fmt.Printf("读取失败: %v\n", err)
		} else if n > 0 {
			fmt.Printf("读取成功 Hex: %s | 字符串: %s\n", hex.EncodeToString(buf[:n]), string(buf[:n]))
		}
	}

	return nil
}

// 【彻底修复】利用全局底层 API 实现完美转换，规避私有字段报错
func parseAddressFromString(addr string) bluetooth.Address {
	addr = strings.ReplaceAll(addr, " ", "")

	// 1. 检查是否为标准的 MAC 地址格式
	macRegex := regexp.MustCompile(`^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$`)
	if macRegex.MatchString(addr) {
		mac, err := bluetooth.ParseMAC(addr)
		if err != nil {
			panic(fmt.Sprintf("解析 MAC 地址失败: %v", err))
		}
		// 核心改动：利用库原生提供的 MAC 地址快速包装生成 Address 结构体
		return bluetooth.Address{MACAddress: bluetooth.MACAddress{MAC: mac}}
	}

	// 2. 针对非 MAC 格式输入，直接提示或回退默认值（规避在 Windows 上调用私有 SetUUID 导致的编译错误）
	panic("当前平台输入格式错误！请确保输入为标准 MAC 地址 (如 11:22:33:AA:BB:CC)")
}

