package tts

import (
	"bytes"
	"context"
	"fmt"
	"game-driver/config"
	"game-driver/leaf"
	"game-driver/pkg/audio"
	"go.uber.org/zap"
	"io"
	"log"
	"sync"
	"time"

	nls "github.com/aliyun/alibabacloud-nls-go-sdk"
)

// AliTTS 阿里云语音合成
// 同一时间只能播放一个 TTS
type AliTTS struct {
	config.AliyunConfig
	tokenResult nls.TokenResult
	mu sync.Mutex // 互斥锁，确保同时只播放一个
}

type result struct {
	Data  io.ReadWriter
	Error error
}

var DefaultTTS = &AliTTS{}

// onTaskFailed TTS 合成失败回调
func (tts *AliTTS) onTaskFailed(text string, param interface{}) {
	p, _ := param.(*result)
	p.Error = fmt.Errorf("语音合成异常: %v", text)
}

// onSynthesisResult TTS 合成数据回调
func (tts *AliTTS) onSynthesisResult(data []byte, param interface{}) {
	p, _ := param.(*result)
	p.Data.Write(data)
}

// Sound 播放 TTS
// 如果已有 TTS 在播放，会等待当前播放完成后再播放新的
func (tts *AliTTS) Sound(ctx context.Context, text string) {
	if text == "" {
		return
	}

	zap.S().Infof("[TTS] 开始播放: %s", text)

	buf, err := tts.Get(ctx, text)
	if err != nil {
		zap.S().Errorw("[TTS] 合成失败", "text", text, "error", err)
		return
	}

	size := buf.(*bytes.Buffer).Len()
	zap.S().Debugf("[TTS] 合成成功: %s (%d字节)", text, size)

	// 获取锁，阻塞等待直到可以播放
	zap.S().Debugf("[TTS] 等待播放锁: %s", text)
	tts.mu.Lock()
	defer tts.mu.Unlock()

	audio.PlayWav(ctx, buf)

	// 检查是否被取消
	if ctx.Err() != nil {
		zap.S().Debugf("[TTS] 播放被取消: %s", text)
		return
	}

	zap.S().Infof("[TTS] 播放完成: %s", text)
}

// getToken 获取阿里云 TTS Token
func (tts *AliTTS) getToken() error {
	// Token 未过期则复用
	if tts.tokenResult.ExpireTime != 0 && time.Unix(tts.tokenResult.ExpireTime, 0).After(time.Now()) {
		return nil
	}

	tts.tokenResult = nls.TokenResult{}
	resultMessage, err := nls.GetToken("cn-shanghai", "nls-meta.cn-shanghai.aliyuncs.com", tts.AccessKeyID, tts.AccessKeySecret, "2019-02-28")
	if err != nil {
		return err
	}

	if resultMessage.ErrMsg != "" {
		return fmt.Errorf("获取Token失败: %s", resultMessage.ErrMsg)
	}

	tts.tokenResult = resultMessage.TokenResult
	return nil
}

// Get 合成语音文本（内部方法）
func (tts *AliTTS) Get(ctx context.Context, text string) (io.Reader, error) {
	param := nls.DefaultSpeechSynthesisParam()
	param.Volume = tts.Volume
	param.Voice = tts.Voice
	param.SpeechRate = tts.SpeechRate

	if err := tts.getToken(); err != nil {
		return nil, err
	}

	connectConfig := nls.NewConnectionConfigWithToken(nls.DEFAULT_URL, tts.AppKey, tts.tokenResult.Id)
	logger := nls.NewNlsLogger(leaf.DefaultWriter, "", log.LstdFlags|log.Ltime)
	logger.SetLogSil(false)
	logger.SetDebug(true)

	ttsData := &result{
		Data: &bytes.Buffer{},
	}

	synthesis, err := nls.NewSpeechSynthesis(
		connectConfig, logger, false,
		tts.onTaskFailed, tts.onSynthesisResult, nil,
		nil, nil, ttsData,
	)
	if err != nil {
		return ttsData.Data, err
	}
	defer synthesis.Shutdown()

	ch, err := synthesis.Start(text, param, nil)
	if err != nil {
		return ttsData.Data, err
	}

	// 等待合成完成
	select {
	case done := <-ch:
		if !done {
			return ttsData.Data, fmt.Errorf("TTS合成失败")
		}
		size := ttsData.Data.(*bytes.Buffer).Len()
		if size == 0 {
			return ttsData.Data, fmt.Errorf("TTS合成数据为空")
		}
		return ttsData.Data, nil

	case <-time.After(time.Duration(tts.Timeout) * time.Second):
		return ttsData.Data, fmt.Errorf("TTS合成超时")

	case <-ctx.Done():
		return ttsData.Data, fmt.Errorf("请求被取消")
	}
}

// New 创建 TTS 实例
func New(config config.AliyunConfig) *AliTTS {
	return &AliTTS{
		AliyunConfig: config,
	}
}
