package lora

import (
	"bytes"
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"strconv"
	"sync"
	"time"

	"git.huangwc.com/pig/pig-farm-controller/internal/infra/config"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport/proto"

	"github.com/google/uuid"
	"github.com/tarm/serial"
	gproto "google.golang.org/protobuf/proto"
)

// transportState 定义了传输层的内部状态
type transportState int

const (
	stateIdle      transportState = iota // 空闲状态
	stateReceiving                       // 接收状态：正在接收一个（可能分片的）消息
	stateSending                         // 发送状态：正在发送一个（可能分片的）消息
)

// message 是一个内部结构，用于封装一个完整的、已重组的消息及其元数据
type message struct {
	SourceAddr string // 源地址
	DestAddr   string // 目标地址
	Payload    []byte // 有效载荷
}

// LoRaMeshUartPassthroughTransport 实现了 transport.Communicator 和 transport.Listener 接口
type LoRaMeshUartPassthroughTransport struct {
	selfCtx context.Context
	config  config.LoraMeshConfig
	port    *serial.Port
	handler transport.UpstreamHandler // 依赖注入的统一业务处理器

	mu    sync.Mutex // 用于保护对外的公共方法（如Send）的并发调用
	state transportState

	stopChan chan struct{}     // 用于优雅地停止worker协程
	wg       sync.WaitGroup    // 用于等待worker协程完全退出
	sendChan chan *sendRequest // 发送任务的请求通道

	// --- 接收与重组相关 ---
	reassemblyBuffers   map[uint16]*reassemblyBuffer // 键为源地址SourceAddr，值为对应的重组缓冲区
	currentRecvSource   uint16                       // 当前正在接收的源地址
	reassemblyTimeout   *time.Timer                  // 分片重组的超时定时器
	reassemblyTimeoutCh chan uint16                  // 当超时触发时，用于传递源地址
}

// sendRequest 封装了一次发送请求
type sendRequest struct {
	address string
	payload []byte
	result  chan *sendResultTuple
}

// sendResultTuple 用于在通道中安全地传递Send方法的返回值
type sendResultTuple struct {
	result *transport.SendResult
	err    error
}

// reassemblyBuffer 用于缓存和重组来自同一源的分片
type reassemblyBuffer struct {
	chunks         map[uint8][]byte // 键为当前包序号CurrentChunk
	totalChunks    uint8
	receivedChunks int
}

// NewLoRaMeshUartPassthroughTransport 创建一个新的 LoRaMeshUartPassthroughTransport 实例
func NewLoRaMeshUartPassthroughTransport(
	ctx context.Context,
	config config.LoraMeshConfig,
	handler transport.UpstreamHandler,
) (*LoRaMeshUartPassthroughTransport, error) {
	c := &serial.Config{
		Name:        config.UARTPort,
		Baud:        config.BaudRate,
		ReadTimeout: time.Millisecond * time.Duration(config.Timeout),
	}

	port, err := serial.OpenPort(c)
	if err != nil {
		return nil, fmt.Errorf("无法打开串口 %s: %w", config.UARTPort, err)
	}

	t := &LoRaMeshUartPassthroughTransport{
		selfCtx:             logs.AddCompName(ctx, "LoRaMeshUartPassthroughTransport"),
		config:              config,
		port:                port,
		handler:             handler,
		state:               stateIdle,
		stopChan:            make(chan struct{}),
		sendChan:            make(chan *sendRequest),
		reassemblyBuffers:   make(map[uint16]*reassemblyBuffer),
		reassemblyTimeoutCh: make(chan uint16, 1),
	}

	return t, nil
}

// Listen 启动后台监听协程（非阻塞）
func (t *LoRaMeshUartPassthroughTransport) Listen(ctx context.Context) error {
	// 注意：这里的 loraCtx 是从 selfCtx 派生的，因为它代表了这个组件自身的生命周期
	loraCtx, logger := logs.Trace(ctx, t.selfCtx, "Listen")
	t.wg.Add(1)
	go t.workerLoop(loraCtx)
	logger.Info("LoRa Mesh 传输层工作协程已启动")
	return nil
}

// Send 将发送任务提交给worker协程
func (t *LoRaMeshUartPassthroughTransport) Send(ctx context.Context, address string, payload []byte) (*transport.SendResult, error) {
	t.mu.Lock()
	defer t.mu.Unlock()

	resultChan := make(chan *sendResultTuple, 1)
	req := &sendRequest{
		address: address,
		payload: payload,
		result:  resultChan,
	}

	select {
	case t.sendChan <- req:
		// 等待worker协程处理完毕
		res := <-resultChan
		return res.result, res.err
	case <-t.stopChan:
		return nil, fmt.Errorf("传输层正在停止")
	}
}

// Stop 停止传输层
func (t *LoRaMeshUartPassthroughTransport) Stop(ctx context.Context) error {
	close(t.stopChan)
	t.wg.Wait()
	return t.port.Close()
}

// workerLoop 是核心的状态机和调度器
func (t *LoRaMeshUartPassthroughTransport) workerLoop(ctx context.Context) {
	loraCtx, logger := logs.Trace(ctx, t.selfCtx, "workerLoop")

	defer t.wg.Done()

	readBuffer := make([]byte, 1024)
	parserBuffer := new(bytes.Buffer)

	for {
		// 1. 检查是否需要停止 (优先检查，以便快速退出)
		select {
		case <-t.stopChan:
			if t.reassemblyTimeout != nil {
				t.reassemblyTimeout.Stop()
			}
			logger.Info("LoRa传输层工作协程已停止")
			return
		default:
		}

		// 2. 尝试从串口读取数据
		n, err := t.port.Read(readBuffer)
		if n > 0 {
			parserBuffer.Write(readBuffer[:n])
		}
		if err != nil && err != io.EOF {
			// 忽略预期的超时错误(io.EOF)，只记录真正的IO错误
			logger.Errorf("从串口读取数据时发生错误: %v", err)
		}

		// 3. 循环解析缓冲区中的完整物理帧
		for {
			frame := t.parseCompleteFrame(parserBuffer)
			if frame == nil {
				break // 缓冲区中没有更多完整帧了
			}
			t.handleFrame(loraCtx, frame)
		}

		// 4. 根据当前状态执行主要逻辑
		switch t.state {
		case stateIdle:
			t.runIdleState(loraCtx)
		case stateReceiving:
			t.runReceivingState(loraCtx)
		default:
		}
	}
}

// runIdleState 处理空闲状态下的逻辑，主要是检查并启动发送任务
func (t *LoRaMeshUartPassthroughTransport) runIdleState(ctx context.Context) {
	loraCtx, _ := logs.Trace(ctx, t.selfCtx, "runIdleState")

	select {
	case req := <-t.sendChan:
		t.state = stateSending
		// 此处为阻塞式发送
		result, err := t.executeSend(loraCtx, req)
		req.result <- &sendResultTuple{result: result, err: err}
		t.state = stateIdle
	default:
		// 没有发送任务，保持空闲
	}
}

// runReceivingState 处理接收状态下的逻辑，主要是检查超时
func (t *LoRaMeshUartPassthroughTransport) runReceivingState(ctx context.Context) {
	_, logger := logs.Trace(ctx, t.selfCtx, "runReceivingState")
	select {
	case sourceAddr := <-t.reassemblyTimeoutCh:
		logger.Warnw("接收消息超时", "sourceAddr", fmt.Sprintf("0x%04X", sourceAddr))
		delete(t.reassemblyBuffers, sourceAddr)
		t.state = stateIdle
	default:
		// 等待更多分片或超时
	}
}

// executeSend 执行完整的发送流程（分片、构建、写入）
func (t *LoRaMeshUartPassthroughTransport) executeSend(ctx context.Context, req *sendRequest) (*transport.SendResult, error) {
	_, logger := logs.Trace(ctx, t.selfCtx, "executeSend")
	chunks := splitPayload(req.payload, t.config.MaxChunkSize)
	totalChunks := uint8(len(chunks))

	destAddr, err := strconv.ParseUint(req.address, 16, 16)
	if err != nil {
		return nil, fmt.Errorf("无效的目标地址: %s", req.address)
	}

	for i, chunk := range chunks {
		currentChunk := uint8(i)
		frame := new(bytes.Buffer)
		frame.WriteByte(0xED)                                   // 帧头
		frame.WriteByte(uint8(len(chunk) + 2))                  // 数据长度 = 数据块 + 2 (总包数+当前包序号)
		binary.Write(frame, binary.BigEndian, uint16(destAddr)) // 目标地址
		frame.WriteByte(totalChunks)                            // 总包数
		frame.WriteByte(currentChunk)                           // 当前包序号
		frame.Write(chunk)                                      // 数据块

		logger.Debugw("构建LoRa数据包", "bytes", frame.Bytes())
		_, err := t.port.Write(frame.Bytes())
		if err != nil {
			return nil, fmt.Errorf("写入串口失败: %w", err)
		}
	}

	msgID := uuid.New().String()
	// LoRa mesh 是单向通信, 发送方不知道也不关心接收方是否收到, 所以发送成功就当作接收成功
	acknowledgedAt := time.Now()
	receivedSuccess := true
	return &transport.SendResult{MessageID: msgID, AcknowledgedAt: &acknowledgedAt, ReceivedSuccess: &receivedSuccess}, nil
}

// handleFrame 处理一个从串口解析出的完整物理帧
func (t *LoRaMeshUartPassthroughTransport) handleFrame(ctx context.Context, frame []byte) {
	reqCtx, logger := logs.Trace(ctx, t.selfCtx, "handleFrame")
	if len(frame) < 8 {
		logger.Warnw("收到了一个无效长度的帧", "length", len(frame))
		return
	}

	destAddr := binary.BigEndian.Uint16(frame[2:4])
	totalChunks := frame[4]
	currentChunk := frame[5]
	sourceAddr := binary.BigEndian.Uint16(frame[len(frame)-2:])
	chunkData := frame[6 : len(frame)-2]

	// 如果是单包消息
	if totalChunks == 1 {
		msg := &message{
			SourceAddr: fmt.Sprintf("%04X", sourceAddr),
			DestAddr:   fmt.Sprintf("%04X", destAddr),
			Payload:    chunkData,
		}
		// 使用分离的上下文进行异步处理
		detachedCtx := logs.DetachContext(reqCtx)
		go t.handleUpstreamMessage(detachedCtx, msg)
		return
	}

	// --- 处理分片消息 ---
	switch t.state {
	case stateIdle:
		if currentChunk == 0 {
			t.state = stateReceiving
			t.currentRecvSource = sourceAddr
			t.reassemblyBuffers[sourceAddr] = &reassemblyBuffer{
				chunks:         make(map[uint8][]byte),
				totalChunks:    totalChunks,
				receivedChunks: 0,
			}
			t.reassemblyBuffers[sourceAddr].chunks[currentChunk] = chunkData
			t.reassemblyBuffers[sourceAddr].receivedChunks++

			if t.reassemblyTimeout != nil {
				t.reassemblyTimeout.Stop()
			}
			t.reassemblyTimeout = time.AfterFunc(time.Duration(t.config.ReassemblyTimeout)*time.Second, func() {
				t.reassemblyTimeoutCh <- sourceAddr
			})
		} else {
			logger.Warnw("在空闲状态下收到了一个非首包分片，已忽略", "sourceAddr", fmt.Sprintf("0x%04X", sourceAddr))
		}

	case stateReceiving:
		if sourceAddr != t.currentRecvSource {
			logger.Warnw("正在接收数据时，收到了另一个源的分片，已忽略",
				"currentSource", fmt.Sprintf("0x%04X", t.currentRecvSource),
				"newSource", fmt.Sprintf("0x%04X", sourceAddr),
			)
			return
		}

		buffer, ok := t.reassemblyBuffers[sourceAddr]
		if !ok {
			logger.Errorw("内部错误: 处于接收状态，但没有找到缓冲区", "sourceAddr", fmt.Sprintf("0x%04X", sourceAddr))
			t.state = stateIdle // 重置状态
			return
		}

		// 存入分片并重置超时
		buffer.chunks[currentChunk] = chunkData
		buffer.receivedChunks++
		t.reassemblyTimeout.Reset(time.Duration(t.config.ReassemblyTimeout) * time.Second)

		// 检查是否已全部收到
		if buffer.receivedChunks == int(buffer.totalChunks) {
			t.reassemblyTimeout.Stop()

			// 重组消息
			fullPayload := new(bytes.Buffer)
			for i := 0; i < int(buffer.totalChunks); i++ {
				fullPayload.Write(buffer.chunks[uint8(i)])
			}

			msg := &message{
				SourceAddr: fmt.Sprintf("%04X", sourceAddr),
				DestAddr:   fmt.Sprintf("%04X", destAddr),
				Payload:    fullPayload.Bytes(),
			}
			// 使用分离的上下文进行异步处理
			detachedCtx := logs.DetachContext(reqCtx)
			go t.handleUpstreamMessage(detachedCtx, msg)

			// 清理并返回空闲状态
			delete(t.reassemblyBuffers, sourceAddr)
			t.state = stateIdle
		}
	default:
		logger.Errorw("内部错误: 状态机处于未知状态", "state", t.state)
	}
}

// handleUpstreamMessage 在独立的协程中处理单个上行的、完整的消息。
// 【已重构】此方法现在只负责解析和委托，不包含任何业务逻辑。
func (t *LoRaMeshUartPassthroughTransport) handleUpstreamMessage(ctx context.Context, msg *message) {
	reqCtx, logger := logs.Trace(ctx, t.selfCtx, "handleUpstreamMessage")
	logger.Infow("开始适配上行消息并委托", "sourceAddr", msg.SourceAddr)

	// 1. 解析外层 "信封"
	var instruction proto.Instruction
	if err := gproto.Unmarshal(msg.Payload, &instruction); err != nil {
		logger.Errorw("解析上行 Instruction Protobuf 失败",
			"sourceAddr", msg.SourceAddr,
			"error", err,
			"rawData", fmt.Sprintf("%x", msg.Payload),
		)
		return
	}

	// 2. 委托给统一处理器
	// 注意：对于 LoRa Mesh，目前只处理业务指令，没有单独的状态或ACK事件。
	if err := t.handler.HandleInstruction(reqCtx, msg.SourceAddr, &instruction); err != nil {
		logger.Errorw("委托上行指令给统一处理器失败",
			"sourceAddr", msg.SourceAddr,
			"error", err,
		)
	}
}

// parseCompleteFrame 实现粘包和半包处理
func (t *LoRaMeshUartPassthroughTransport) parseCompleteFrame(buffer *bytes.Buffer) []byte {
	for {
		headerIndex := bytes.IndexByte(buffer.Bytes(), 0xED)
		if headerIndex == -1 {
			return nil
		}
		buffer.Next(headerIndex)

		if buffer.Len() < 2 {
			return nil
		}

		lengthField := buffer.Bytes()[1]
		frameLength := 1 + 1 + 2 + int(lengthField) + 2

		if buffer.Len() < frameLength {
			return nil
		}

		return buffer.Next(frameLength)
	}
}

// splitPayload 将数据块按最大长度进行切分
func splitPayload(payload []byte, maxChunkSize int) [][]byte {
	if len(payload) == 0 {
		return [][]byte{{}}
	}

	var chunks [][]byte
	for i := 0; i < len(payload); i += maxChunkSize {
		end := i + maxChunkSize
		if end > len(payload) {
			end = len(payload)
		}
		chunks = append(chunks, payload[i:end])
	}
	return chunks
}
