package asr

import (
	"context"
	"encoding/binary"
	"os"
	"strings"
	"testing"
	"time"
)

// TestGummyLive 9A gummy 实测（需真实凭证，无则跳过）：
//
//	rbw get dashscope-api-key | { read -r K; DASHSCOPE_API_KEY="$K" GUMMY_TEST_WAV=/path/16k.wav go test ./internal/asr -run TestGummyLive -v; }
func TestGummyLive(t *testing.T) {
	key := os.Getenv("DASHSCOPE_API_KEY")
	wav := os.Getenv("GUMMY_TEST_WAV")
	if key == "" || wav == "" {
		t.Skip("DASHSCOPE_API_KEY / GUMMY_TEST_WAV 未设置，跳过 gummy 实测")
	}
	pcm, err := readWavDataT(t, wav)
	if err != nil {
		t.Fatalf("读 wav: %v", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()
	sess, err := NewGummy(key).StartSession(ctx, SessionConfig{SampleRate: 16000, SessionID: "live-test"})
	if err != nil {
		t.Fatalf("StartSession: %v", err)
	}

	var partials, finals int
	var lastFinal string
	var maxEndMs int64
	done := make(chan struct{})
	go func() {
		defer close(done)
		for r := range sess.Results() {
			if r.Err != nil {
				t.Errorf("result err: %v", r.Err)
				return
			}
			if r.IsFinal {
				finals++
				lastFinal = r.Text
			} else {
				partials++
			}
			if r.EndTimeMs > maxEndMs {
				maxEndMs = r.EndTimeMs
			}
		}
	}()

	for off := 0; off < len(pcm); off += 3200 {
		end := min(off+3200, len(pcm))
		if err := sess.SendAudio(pcm[off:end]); err != nil {
			t.Fatalf("SendAudio: %v", err)
		}
		time.Sleep(50 * time.Millisecond)
	}
	_ = sess.Close()
	<-done

	if partials == 0 || finals == 0 {
		t.Fatalf("期望 partial>0 且 final>0，实得 partial=%d final=%d", partials, finals)
	}
	if strings.TrimSpace(lastFinal) == "" {
		t.Fatal("final 文本为空")
	}
	audioMs := int64(len(pcm)) * 1000 / 32000
	if maxEndMs <= 0 || maxEndMs > audioMs+3000 {
		t.Fatalf("provider 时间戳异常: end=%dms audio=%dms", maxEndMs, audioMs)
	}
	t.Logf("partials=%d finals=%d end=%dms final=%q", partials, finals, maxEndMs, lastFinal)
}

func readWavDataT(t *testing.T, path string) ([]byte, error) {
	t.Helper()
	b, err := os.ReadFile(path)
	if err != nil {
		return nil, err
	}
	off := 12
	for off+8 <= len(b) {
		id := string(b[off : off+4])
		size := int(binary.LittleEndian.Uint32(b[off+4 : off+8]))
		if id == "data" {
			return b[off+8 : min(off+8+size, len(b))], nil
		}
		off += 8 + size
	}
	return nil, os.ErrInvalid
}
