package stt

import (
	"context"
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"
)

// sahte motor: gelen baytları ve mime tipini kaydeder.
type fakeRec struct {
	text    string
	err     error
	gotData []byte
	gotMime string
	calls   int
}

func (f *fakeRec) Transcribe(_ context.Context, audio []byte, mime string) (string, error) {
	f.calls++
	f.gotData = append([]byte(nil), audio...)
	f.gotMime = mime
	return f.text, f.err
}

func TestService_StopTranscribes(t *testing.T) {
	rec := &fakeRec{text: "  merhaba\ndünya  "}
	s := New(rec, Limits{})

	s.Start("s1", "audio/webm")
	s.Append("s1", []byte("AAA"))
	s.Append("s1", []byte("BBB"))
	text, dur, err := s.Stop(context.Background(), "s1")

	if err != nil {
		t.Fatal(err)
	}
	if text != "merhaba dünya" {
		t.Errorf("metin temizlenmedi: %q", text)
	}
	if rec.calls != 1 || string(rec.gotData) != "AAABBB" {
		t.Errorf("motor çağrısı: calls=%d data=%q", rec.calls, rec.gotData)
	}
	if rec.gotMime != "audio/webm" {
		t.Errorf("mime taşınmadı: %q", rec.gotMime)
	}
	if dur < 0 {
		t.Errorf("süre negatif: %v", dur)
	}
}

func TestService_Errors(t *testing.T) {
	s := New(&fakeRec{}, Limits{})
	if _, _, err := s.Stop(context.Background(), "yok"); !errors.Is(err, ErrNotStarted) {
		t.Errorf("başlatılmamış: %v", err)
	}
	s.Start("s2", "audio/webm")
	if _, _, err := s.Stop(context.Background(), "s2"); !errors.Is(err, ErrEmptyAudio) {
		t.Errorf("boş ses: %v", err)
	}
	if s.Enabled() != true {
		t.Error("motor varken Enabled true olmalı")
	}
	if (*Service)(nil).Enabled() {
		t.Error("nil servis etkin olmamalı")
	}
	var nilSvc *Service
	if _, _, err := nilSvc.Stop(context.Background(), "x"); !errors.Is(err, ErrNoRecognizer) {
		t.Errorf("nil servis: %v", err)
	}
}

func TestService_TruncatesOverLimit(t *testing.T) {
	rec := &fakeRec{text: "x"}
	s := New(rec, Limits{MaxBytes: 4, MaxSeconds: 60})

	s.Start("s3", "audio/webm")
	s.Append("s3", []byte("1234567890")) // sınırdan büyük
	if _, _, err := s.Stop(context.Background(), "s3"); !errors.Is(err, ErrTooLong) {
		t.Errorf("sınır aşımı bildirilmeliydi: %v", err)
	}
	if len(rec.gotData) > 4 {
		t.Errorf("bellek sınırı uygulanmadı: %d bayt", len(rec.gotData))
	}
}

func TestService_DurationLimit(t *testing.T) {
	rec := &fakeRec{text: "x"}
	s := New(rec, Limits{MaxSeconds: 1})
	// Saat İLERLEMELİ: sabit bir değer dönen sahte saat farkı üretmez (ilk yazımda
	// test bu yüzden "sınır uygulanmadı" diye düştü).
	base := time.Now()
	calls := 0
	s.clock = func() time.Time {
		calls++
		if calls == 1 {
			return base
		}
		return base.Add(2 * time.Second)
	}

	s.Start("s4", "audio/webm")
	s.Append("s4", []byte("12"))
	if _, _, err := s.Stop(context.Background(), "s4"); !errors.Is(err, ErrTooLong) {
		t.Errorf("süre sınırı uygulanmadı: %v", err)
	}
}

func TestService_CancelDropsRecording(t *testing.T) {
	rec := &fakeRec{text: "x"}
	s := New(rec, Limits{})
	s.Start("s5", "audio/webm")
	s.Cancel("s5")
	if _, _, err := s.Stop(context.Background(), "s5"); !errors.Is(err, ErrNotStarted) {
		t.Errorf("vazgeçme kaydı silmeliydi: %v", err)
	}
}

func TestService_RestartReplacesRecording(t *testing.T) {
	rec := &fakeRec{text: "yeni"}
	s := New(rec, Limits{})
	s.Start("s6", "audio/webm")
	s.Append("s6", []byte("eski"))
	s.Start("s6", "audio/webm") // yeniden başlat
	s.Append("s6", []byte("yeni"))
	if _, _, err := s.Stop(context.Background(), "s6"); err != nil {
		t.Fatal(err)
	}
	if string(rec.gotData) != "yeni" {
		t.Errorf("önceki veri karışmamalıydı: %q", rec.gotData)
	}
}

func TestWhisper_Transcribes(t *testing.T) {
	var gotField, gotFile string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if err := r.ParseMultipartForm(4 << 20); err != nil {
			t.Errorf("multipart okunamadı: %v", err)
		}
		gotField = r.FormValue("language")
		f, hdr, err := r.FormFile("file")
		if err == nil {
			defer f.Close()
			gotFile = hdr.Filename
		}
		_, _ = w.Write([]byte(`{"text":"merhaba"}`))
	}))
	defer srv.Close()

	w := NewWhisper(Options{URL: srv.URL + "/inference", Language: "tr"})
	if w == nil {
		t.Fatal("whisper oluşturulamadı")
	}
	out, err := w.Transcribe(context.Background(), []byte("AUDIO"), "audio/webm")
	if err != nil {
		t.Fatal(err)
	}
	if out != "merhaba" {
		t.Errorf("metin: %q", out)
	}
	if gotField != "tr" {
		t.Errorf("dil alanı gitmedi: %q", gotField)
	}
	if !strings.HasSuffix(gotFile, ".webm") {
		t.Errorf("uzantı yanlış: %q", gotFile)
	}
}

func TestWhisper_Unavailable(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
	}))
	defer srv.Close()

	w := NewWhisper(Options{URL: srv.URL})
	if _, err := w.Transcribe(context.Background(), []byte("x"), "audio/webm"); !errors.Is(err, ErrUnavailable) {
		t.Errorf("500 geçici hata olmalı: %v", err)
	}
}

func TestNewWhisper_NilWhenURLEmpty(t *testing.T) {
	if NewWhisper(Options{}) != nil {
		t.Error("URL yoksa nil dönmeli (motor yapılandırılmamış)")
	}
}

func TestSanitize(t *testing.T) {
	if got := Sanitize("  bir\niki   üç "); got != "bir iki üç" {
		t.Errorf("beklenmeyen temizleme: %q", got)
	}
	if got := Sanitize("\n\t "); got != "" {
		t.Errorf("boş kalmalıydı: %q", got)
	}
}

// ---- OpenAI-uyumlu motor (bulut / faster-whisper sunucusu) ----

func TestWhisperAPITranscribe(t *testing.T) {
	var gotAuth, gotFile, gotModel, gotLang string
	var gotAudio []byte
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		gotAuth = r.Header.Get("Authorization")
		if err := r.ParseMultipartForm(4 << 20); err != nil {
			t.Errorf("multipart okunamadı: %v", err)
			return
		}
		f, hdr, err := r.FormFile("file")
		if err != nil {
			t.Errorf("file alanı yok: %v", err)
			return
		}
		defer f.Close()
		gotFile = hdr.Filename
		gotAudio, _ = io.ReadAll(f)
		gotModel = r.FormValue("model")
		gotLang = r.FormValue("language")
		_, _ = w.Write([]byte(`{"text":"bugün hava nasıl"}`))
	}))
	defer srv.Close()

	rec := NewWhisperAPI(Options{URL: srv.URL, APIKey: "sk-gizli", Model: "whisper-1", Language: "tr"})
	text, err := rec.Transcribe(context.Background(), []byte("WAVDATA"), "audio/webm")
	if err != nil {
		t.Fatalf("Transcribe hatası: %v", err)
	}
	if text != "bugün hava nasıl" {
		t.Errorf("metin = %q", text)
	}
	if gotAuth != "Bearer sk-gizli" {
		t.Errorf("Authorization = %q", gotAuth)
	}
	if gotFile != "audio.webm" {
		t.Errorf("dosya adı = %q, beklenen audio.webm (uzantı içerik türünden)", gotFile)
	}
	if string(gotAudio) != "WAVDATA" {
		t.Errorf("ses = %q", gotAudio)
	}
	if gotModel != "whisper-1" || gotLang != "tr" {
		t.Errorf("model=%q language=%q", gotModel, gotLang)
	}
}

// Uyumlu sunucular "transcription" alanını da kullanabilir; ikisi de okunmalı.
func TestWhisperAPIAlternatifAlan(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte(`{"transcription":"alternatif alan"}`))
	}))
	defer srv.Close()

	rec := NewWhisperAPI(Options{URL: srv.URL})
	text, err := rec.Transcribe(context.Background(), []byte("AUDIO"), "audio/webm")
	if err != nil {
		t.Fatalf("Transcribe hatası: %v", err)
	}
	if text != "alternatif alan" {
		t.Errorf("metin = %q", text)
	}
}

func TestWhisperAPIHatalar(t *testing.T) {
	if NewWhisperAPI(Options{}) != nil {
		t.Error("URL boşsa nil dönmeli")
	}
	rec := NewWhisperAPI(Options{URL: "http://127.0.0.1:1/x"})
	if _, err := rec.Transcribe(context.Background(), nil, "audio/webm"); err != ErrEmptyAudio {
		t.Errorf("boş ses hatası = %v, beklenen ErrEmptyAudio", err)
	}
	if _, err := rec.Transcribe(context.Background(), []byte("A"), "audio/webm"); !errors.Is(err, ErrUnavailable) {
		t.Errorf("kapalı motor hatası = %v, beklenen ErrUnavailable", err)
	}

	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusTooManyRequests)
		_, _ = w.Write([]byte(" kota doldu "))
	}))
	defer srv.Close()
	rec2 := NewWhisperAPI(Options{URL: srv.URL})
	_, err := rec2.Transcribe(context.Background(), []byte("A"), "audio/webm")
	if !errors.Is(err, ErrUnavailable) {
		t.Errorf("HTTP 429 hatası = %v, beklenen ErrUnavailable", err)
	}
	// Kullanıcıya gösterilen hata gövdesi boş olmamalı (ilk 512 bayt okunur).
	if err != nil && !strings.Contains(err.Error(), "kota doldu") {
		t.Errorf("hata gövdesi kayboldu: %v", err)
	}
}

// Yanıtta metin yoksa hata verilir (sessizce boş metin dönmek yanıltıcı olurdu).
func TestWhisperAPIYanittaMetinYok(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte(`{"ok":true}`))
	}))
	defer srv.Close()
	rec := NewWhisperAPI(Options{URL: srv.URL})
	if _, err := rec.Transcribe(context.Background(), []byte("A"), "audio/webm"); !errors.Is(err, ErrUnavailable) {
		t.Errorf("hata = %v, beklenen ErrUnavailable", err)
	}
}
