package handler

import (
	"context"
	"log"
	"time"

	"github.com/qy/hw-ws-service/internal/audio"
	"github.com/qy/hw-ws-service/internal/connection"
	"github.com/qy/hw-ws-service/internal/rtcclient"
)

// HandleStory 处理硬件发来的 {"type":"story"} 指令。
// 在独立 goroutine 中调用，不阻塞消息读取循环。
func HandleStory(conn *connection.Connection, client *rtcclient.Client) {
	tag := "[story][" + conn.DeviceID + "]"

	// 整个故事播放流程最长允许 10 分钟
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	defer cancel()

	// 1. 通知硬件：TTS 开始
	if err := conn.SendJSON(map[string]string{"type": "tts", "state": "start"}); err != nil {
		log.Printf("%s send start failed: %v", tag, err)
		return
	}

	// 确保异常退出时也发送 stop，避免硬件卡住
	defer func() {
		conn.StopPlayback()
		conn.SendJSON(map[string]string{"type": "tts", "state": "stop"}) //nolint:errcheck
	}()

	// 2. 调用 RTC 后端获取故事
	story, err := client.FetchStoryByMAC(ctx, conn.DeviceID)
	if err != nil {
		log.Printf("%s fetch story error: %v", tag, err)
		return
	}
	if story == nil {
		log.Printf("%s no story available", tag)
		return
	}
	log.Printf("%s playing: %s", tag, story.Title)

	// 3. 获取 Opus 帧：优先使用预转码数据，否则实时 ffmpeg 转码
	var frames [][]byte
	if story.OpusURL != "" {
		frames, err = audio.FetchOpusFrames(ctx, story.OpusURL)
		if err != nil {
			log.Printf("%s fetch pre-converted opus failed, fallback to ffmpeg: %v", tag, err)
			frames = nil // 确保 fallback
		} else {
			log.Printf("%s loaded %d pre-converted frames (~%.1fs)", tag, len(frames),
				float64(len(frames)*audio.FrameDurationMs)/1000)
		}
	}
	if frames == nil {
		frames, err = audio.MP3URLToOpusFrames(ctx, story.AudioURL)
		if err != nil {
			log.Printf("%s audio convert error: %v", tag, err)
			return
		}
		log.Printf("%s converted %d frames (~%.1fs)", tag, len(frames),
			float64(len(frames)*audio.FrameDurationMs)/1000)
	}

	// 4. 通知硬件：句子开始（发送故事标题）
	if err := conn.SendJSON(map[string]any{
		"type":  "tts",
		"state": "sentence_start",
		"text":  story.Title,
	}); err != nil {
		log.Printf("%s send sentence_start failed: %v", tag, err)
		return
	}

	// 5. 开始播放，获取打断 channel
	abortCh := conn.StartPlayback()

	// 6. 流控推送 Opus 帧
	SendOpusStream(conn, frames, abortCh)

	log.Printf("%s playback finished", tag)
	// defer 会发送 stop 并调用 StopPlayback
}
