// Package connection 管理单个 ESP32 硬件 WebSocket 连接的状态。
package connection

import (
	"encoding/json"
	"fmt"
	"sync"

	"github.com/gorilla/websocket"
)

// Connection 保存单个硬件连接的状态，所有方法并发安全。
type Connection struct {
	WS       *websocket.Conn
	DeviceID  string // MAC 地址，来自 URL 参数 device-id
	ClientID  string // 来自 URL 参数 client-id
	SessionID string // 握手后分配的会话 ID

	mu          sync.Mutex
	handshaked  bool         // 是否已完成 hello 握手
	isPlaying   bool
	abortCh     chan struct{} // close(abortCh) 通知流控 goroutine 中止播放

	writeMu sync.Mutex // gorilla/websocket 写操作不并发安全，需独立锁
}

// New 创建新连接对象。
func New(ws *websocket.Conn, deviceID, clientID string) *Connection {
	return &Connection{
		WS:       ws,
		DeviceID: deviceID,
		ClientID: clientID,
	}
}

// Handshake 完成 hello 握手，存储 session_id。
func (c *Connection) Handshake(sessionID string) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.SessionID = sessionID
	c.handshaked = true
}

// IsHandshaked 返回连接是否已完成 hello 握手。
func (c *Connection) IsHandshaked() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.handshaked
}

// SendCmd 向硬件发送控制指令，并发安全。
func (c *Connection) SendCmd(action string, params any) error {
	return c.SendJSON(map[string]any{
		"type":   "cmd",
		"action": action,
		"params": params,
	})
}

// StartPlayback 开始新一轮播放，返回 abortCh 供流控 goroutine 监听。
// 若已在播放，先中止上一轮再开始新的。
func (c *Connection) StartPlayback() <-chan struct{} {
	c.mu.Lock()
	defer c.mu.Unlock()

	// 中止上一轮播放（若有）
	if c.isPlaying && c.abortCh != nil {
		close(c.abortCh)
	}

	c.abortCh = make(chan struct{})
	c.isPlaying = true
	return c.abortCh
}

// StopPlayback 结束播放状态。
func (c *Connection) StopPlayback() {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.isPlaying = false
}

// IsPlaying 返回当前是否正在播放。
func (c *Connection) IsPlaying() bool {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.isPlaying
}

// SendJSON 序列化 v 并以文本帧发送给设备，并发安全。
func (c *Connection) SendJSON(v any) error {
	data, err := json.Marshal(v)
	if err != nil {
		return fmt.Errorf("connection: marshal json: %w", err)
	}
	c.writeMu.Lock()
	defer c.writeMu.Unlock()
	return c.WS.WriteMessage(websocket.TextMessage, data)
}

// SendBinary 以二进制帧发送 Opus 数据，并发安全。
func (c *Connection) SendBinary(data []byte) error {
	c.writeMu.Lock()
	defer c.writeMu.Unlock()
	return c.WS.WriteMessage(websocket.BinaryMessage, data)
}

// Close 关闭底层 WebSocket 连接。
func (c *Connection) Close() {
	c.WS.Close()
}
