package tts

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync/atomic"
	"testing"
	"time"

	"go.uber.org/zap"
)

// mockMiMoTTSServer 创建模拟 MiMo TTS API 的 HTTP 服务器。
func mockMiMoTTSServer(t *testing.T, handler http.HandlerFunc) *httptest.Server {
	t.Helper()
	return httptest.NewServer(handler)
}

// buildMiMoTTSResponse 构造 MiMo TTS 非流式响应 JSON。
func buildMiMoTTSResponse(audioData string) []byte {
	resp := mimoTTSResponse{
		Choices: []struct {
			Message struct {
				Audio struct {
					Data string `json:"data"`
				} `json:"audio"`
			} `json:"message"`
		}{
			{
				Message: struct {
					Audio struct {
						Data string `json:"data"`
					} `json:"audio"`
				}{
					Audio: struct {
						Data string `json:"data"`
					}{Data: audioData},
				},
			},
		},
	}
	data, _ := json.Marshal(resp)
	return data
}

func TestMiMoService_SynthesizeStream_Success(t *testing.T) {
	var callCount int32
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		atomic.AddInt32(&callCount, 1)

		if r.Method != http.MethodPost {
			t.Errorf("method = %s, want POST", r.Method)
		}
		if !strings.Contains(r.URL.Path, "/chat/completions") {
			t.Errorf("path = %s, should contain /chat/completions", r.URL.Path)
		}

		// 验证 api-key 认证头
		apiKey := r.Header.Get("api-key")
		if apiKey != "test-key" {
			t.Errorf("api-key = %q, want %q", apiKey, "test-key")
		}

		// 验证请求体
		body, _ := io.ReadAll(r.Body)
		var req mimoTTSRequest
		if err := json.Unmarshal(body, &req); err != nil {
			t.Errorf("unmarshal request: %v", err)
		}
		if req.Model != "mimo-v2.5-tts" {
			t.Errorf("model = %q, want %q", req.Model, "mimo-v2.5-tts")
		}
		if len(req.Messages) != 1 || req.Messages[0].Role != "assistant" {
			t.Errorf("expected 1 assistant message, got %d messages", len(req.Messages))
		}
		if req.Audio.Voice != "冰糖" {
			t.Errorf("voice = %q, want %q", req.Audio.Voice, "冰糖")
		}
		if req.Audio.Format != "mp3" {
			t.Errorf("format = %q, want %q", req.Audio.Format, "mp3")
		}

		// 返回假音频数据（base64 编码）
		audioB64 := base64.StdEncoding.EncodeToString([]byte("fake-mp3-data"))
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(audioB64))
	})
	defer srv.Close()

	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	textStream := sendSentences("你好", "世界", "！")

	ch, err := svc.SynthesizeStream(context.Background(), textStream, Options{
		Voice: "冰糖", OutputFmt: "mp3", SampleRate: 24000,
	})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	var chunks []Chunk
	for c := range ch {
		chunks = append(chunks, c)
	}

	// 应该有 3 个音频 chunk + 1 个 Final 标记
	if len(chunks) != 4 {
		t.Fatalf("got %d chunks, want 4", len(chunks))
	}

	// 验证前 3 个有音频数据，IsLast 为 true（每句结束）
	for i := 0; i < 3; i++ {
		if string(chunks[i].Audio) != "fake-mp3-data" {
			t.Errorf("chunk[%d].Audio = %q, want %q", i, string(chunks[i].Audio), "fake-mp3-data")
		}
		if !chunks[i].IsLast {
			t.Errorf("chunk[%d].IsLast should be true (sentence end)", i)
		}
		if chunks[i].Final {
			t.Errorf("chunk[%d].Final should be false", i)
		}
	}

	// 验证最后一个是 Final（整轮结束）
	if !chunks[3].Final {
		t.Error("last chunk should be Final")
	}
	if chunks[3].Audio != nil {
		t.Error("last chunk Audio should be nil")
	}

	// 验证调用了 3 次 API（3 个句子）
	if atomic.LoadInt32(&callCount) != 3 {
		t.Errorf("API called %d times, want 3", callCount)
	}
}

func TestMiMoService_SynthesizeStream_APIError(t *testing.T) {
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintf(w, "internal error")
	})
	defer srv.Close()

	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	textStream := sendSentences("你好")

	ch, err := svc.SynthesizeStream(context.Background(), textStream, Options{})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	// 应该只有一个 Final chunk（音频被跳过）
	var chunks []Chunk
	for c := range ch {
		chunks = append(chunks, c)
	}

	if len(chunks) != 1 {
		t.Fatalf("got %d chunks, want 1 (Final only)", len(chunks))
	}
	if !chunks[0].Final {
		t.Error("chunk should be Final")
	}
}

func TestMiMoService_SynthesizeStream_Timeout(t *testing.T) {
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		time.Sleep(3 * time.Second)
		audioB64 := base64.StdEncoding.EncodeToString([]byte("late-mp3"))
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(audioB64))
	})
	defer srv.Close()

	// 1 秒超时
	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 1, 30, zap.NewNop().Sugar())

	textStream := sendSentences("很长的句子")

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	ch, err := svc.SynthesizeStream(ctx, textStream, Options{})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	var chunks []Chunk
	for c := range ch {
		chunks = append(chunks, c)
	}

	// 超时后音频被跳过，只有 Final
	if len(chunks) != 1 {
		t.Fatalf("got %d chunks, want 1", len(chunks))
	}
	if !chunks[0].Final {
		t.Error("chunk should be Final")
	}
}

func TestMiMoService_SynthesizeStream_EmptyText(t *testing.T) {
	var callCount int32
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		atomic.AddInt32(&callCount, 1)
		audioB64 := base64.StdEncoding.EncodeToString([]byte("mp3"))
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(audioB64))
	})
	defer srv.Close()

	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	// 空句子应该被跳过
	textStream := sendSentences("", "你好", "")

	ch, err := svc.SynthesizeStream(context.Background(), textStream, Options{})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	var chunks []Chunk
	for c := range ch {
		chunks = append(chunks, c)
	}

	// 只有 "你好" 应该被合成
	if atomic.LoadInt32(&callCount) != 1 {
		t.Errorf("API called %d times, want 1", callCount)
	}

	// 1 个音频（IsLast: true）+ 1 个 Final
	if len(chunks) != 2 {
		t.Fatalf("got %d chunks, want 2", len(chunks))
	}
}

func TestMiMoService_SynthesizeStream_ContextCancelled(t *testing.T) {
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		audioB64 := base64.StdEncoding.EncodeToString([]byte("mp3"))
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(audioB64))
	})
	defer srv.Close()

	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	textStream := make(chan string, 3)
	textStream <- "第一句"
	textStream <- "第二句"
	textStream <- "第三句"
	close(textStream)

	ctx, cancel := context.WithCancel(context.Background())
	// 立即取消
	cancel()

	ch, err := svc.SynthesizeStream(ctx, textStream, Options{})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	// 消费 channel，应该很快结束
	var count int
	for range ch {
		count++
	}
	// 可能收到 0 个或 1 个 chunk，取决于时序
	t.Logf("received %d chunks after context cancel", count)
}

func TestMiMoService_SynthesizeStream_PartialFailure(t *testing.T) {
	var callCount int32
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		n := atomic.AddInt32(&callCount, 1)
		if n == 2 {
			// 第二个句子失败
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintf(w, "error")
			return
		}
		audioB64 := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("mp3-%d", n)))
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(audioB64))
	})
	defer srv.Close()

	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	textStream := sendSentences("第一句", "第二句", "第三句")

	ch, err := svc.SynthesizeStream(context.Background(), textStream, Options{})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	var chunks []Chunk
	for c := range ch {
		chunks = append(chunks, c)
	}

	// 2 个成功音频（IsLast: true）+ 1 个 Final（第二句被跳过）
	if len(chunks) != 3 {
		t.Fatalf("got %d chunks, want 3", len(chunks))
	}
	if !chunks[0].IsLast {
		t.Error("first audio chunk should be IsLast")
	}
	if !chunks[1].IsLast {
		t.Error("second audio chunk should be IsLast")
	}
	if !chunks[len(chunks)-1].Final {
		t.Error("last chunk should be Final")
	}
}

func TestMiMoService_SynthesizeStream_CustomVoice(t *testing.T) {
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		body, _ := io.ReadAll(r.Body)
		var req mimoTTSRequest
		if err := json.Unmarshal(body, &req); err != nil {
			t.Errorf("unmarshal request: %v", err)
		}
		if req.Audio.Voice != "茉莉" {
			t.Errorf("voice = %q, want %q", req.Audio.Voice, "茉莉")
		}
		audioB64 := base64.StdEncoding.EncodeToString([]byte("mp3"))
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(audioB64))
	})
	defer srv.Close()

	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	textStream := sendSentences("你好")

	ch, err := svc.SynthesizeStream(context.Background(), textStream, Options{Voice: "茉莉"})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	for range ch {
	}
}

func TestMiMoService_SynthesizeStream_DefaultVoice(t *testing.T) {
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		body, _ := io.ReadAll(r.Body)
		var req mimoTTSRequest
		if err := json.Unmarshal(body, &req); err != nil {
			t.Errorf("unmarshal request: %v", err)
		}
		// 未指定 voice 时应使用默认 "冰糖"
		if req.Audio.Voice != "冰糖" {
			t.Errorf("voice = %q, want %q (default)", req.Audio.Voice, "冰糖")
		}
		audioB64 := base64.StdEncoding.EncodeToString([]byte("mp3"))
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(audioB64))
	})
	defer srv.Close()

	// 不指定 voice
	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	textStream := sendSentences("你好")

	ch, err := svc.SynthesizeStream(context.Background(), textStream, Options{})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	for range ch {
	}
}

func TestMiMoService_SynthesizeStream_EmptyAudioData(t *testing.T) {
	srv := mockMiMoTTSServer(t, func(w http.ResponseWriter, r *http.Request) {
		// 返回空音频数据
		w.Header().Set("Content-Type", "application/json")
		w.Write(buildMiMoTTSResponse(""))
	})
	defer srv.Close()

	svc := NewMiMoService("test-key", "mimo-v2.5-tts", "冰糖", srv.URL, 5, 30, zap.NewNop().Sugar())

	textStream := sendSentences("你好")

	ch, err := svc.SynthesizeStream(context.Background(), textStream, Options{})
	if err != nil {
		t.Fatalf("SynthesizeStream() error: %v", err)
	}

	var chunks []Chunk
	for c := range ch {
		chunks = append(chunks, c)
	}

	// 空音频数据导致错误，句子被跳过，只有 Final
	if len(chunks) != 1 {
		t.Fatalf("got %d chunks, want 1", len(chunks))
	}
	if !chunks[0].Final {
		t.Error("chunk should be Final")
	}
}
