package tts

import (
	"context"
	"testing"

	"github.com/kami/maven/internal/audio"
	"github.com/kami/maven/internal/worker"
)

type fakeSynthesizer struct{}

func (fakeSynthesizer) Synthesize(_ context.Context, req worker.SynthesizeReq) (worker.SynthesizeResp, error) {
	if req.Text == "" {
		return worker.SynthesizeResp{}, nil
	}
	pcm := make([]byte, 3200)
	for i := 0; i < len(pcm)/2; i++ {
		v := int16(i)
		pcm[i*2] = byte(v)
		pcm[i*2+1] = byte(v >> 8)
	}
	return worker.SynthesizeResp{Audio: audio.Audio{Format: audio.PCM16kMono, Bytes: pcm}}, nil
}

func TestStubProducesValidAudio(t *testing.T) {
	t.Parallel()
	s := NewStub()
	a, err := s.Synthesize(context.Background(), "hi")
	if err != nil {
		t.Fatalf("Stub.Synthesize: %v", err)
	}
	if !a.Format.IsValid() {
		t.Fatalf("format invalid: %+v", a.Format)
	}
	// 200ms @ 16k ⇒ 3200 samples ⇒ 6400 bytes.
	if len(a.Bytes) != 6400 {
		t.Fatalf("bytes len: %d, want 6400", len(a.Bytes))
	}
	// non-empty audio should not be all zeros (sine wave).
	allZero := true
	for _, b := range a.Bytes {
		if b != 0 {
			allZero = false
			break
		}
	}
	if allZero {
		t.Fatalf("Stub audio is all zeros; should be a tone")
	}
	if a.Duration() < 0.19 || a.Duration() > 0.21 {
		t.Fatalf("Duration: got %v, want ~0.2s", a.Duration())
	}
}

func TestStubDifferentTextDifferentFreq(t *testing.T) {
	t.Parallel()
	s := NewStub()
	a, _ := s.Synthesize(context.Background(), "a") // 'a' = 97
	b, _ := s.Synthesize(context.Background(), "z") // 'z' = 122
	// different first byte ⇒ different modulo ⇒ different tones; bytes differ.
	if samePCM(a.Bytes, b.Bytes) {
		t.Fatalf("expected different tones for 'a' vs 'z'")
	}
}

func TestStubEmptyTextStillTones(t *testing.T) {
	t.Parallel()
	s := NewStub()
	a, err := s.Synthesize(context.Background(), "")
	if err != nil {
		t.Fatalf("Stub.Synthesize: %v", err)
	}
	if len(a.Bytes) == 0 {
		t.Fatalf("empty text should still produce tone (audible), got silence")
	}
}

func TestRemoteForwardsToClient(t *testing.T) {
	t.Parallel()
	srv := worker.NewSynthesizerServer(t.TempDir()+"/tts.sock", fakeSynthesizer{})
	if err := srv.Listen(); err != nil {
		t.Fatalf("listen: %v", err)
	}
	defer srv.Close()
	go srv.Serve()

	c := worker.Dial(srv.Path())
	defer c.Close()
	r := NewRemote(c, "ru", "")

	out, err := r.Synthesize(context.Background(), "hello")
	if err != nil {
		t.Fatalf("Remote.Synthesize: %v", err)
	}
	if !out.Format.IsValid() {
		t.Fatalf("invalid format: %+v", out.Format)
	}
	if len(out.Bytes) != 3200 {
		t.Fatalf("bytes len: %d, want 3200", len(out.Bytes))
	}
}

func TestRemoteErrorWraps(t *testing.T) {
	t.Parallel()
	// a transcriber-only server ⇒ ErrUnknownMethod on Synthesize.
	srv := worker.NewServer(t.TempDir()+"/tts.sock", &fakeErrTranscriber{})
	if err := srv.Listen(); err != nil {
		t.Fatalf("listen: %v", err)
	}
	defer srv.Close()
	go srv.Serve()
	c := worker.Dial(srv.Path())
	defer c.Close()
	r := NewRemote(c, "ru", "")
	_, err := r.Synthesize(context.Background(), "hello")
	if err == nil {
		t.Fatalf("want error, got nil")
	}
}

type fakeErrTranscriber struct{}

func (fakeErrTranscriber) Transcribe(context.Context, worker.TranscribeReq) (worker.TranscribeResp, error) {
	return worker.TranscribeResp{}, nil
}

func samePCM(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}
