// Package stt dosyası: OpenAI-uyumlu /v1/audio/transcriptions ucunu konuşur (bulut tanıma).
//
// Bu uç birçok sağlayıcıda var: OpenAI Whisper API, Groq, Together, Azure,
// ayrıca faster-whisper'ın sunucu modu. FIKO'da "bulut" ile "yerel" ayrımı değil,
// PROTOCOL ayrımı önemlidir: uyumlu olan motorlar aynı kodla çalışır.
//
// multipart alan adları OpenAI ile birebir aynıdır: file + model (+ language).
package stt

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"strings"
	"time"
)

type WhisperAPI struct {
	url      string
	apikey   string
	model    string
	language string
	client   *http.Client
}

// NewWhisperAPI tanım oluşturur. URL boşsa nil döner (STT yapılandırılmamış demektir).
func NewWhisperAPI(opt Options) *WhisperAPI {
	url := strings.TrimSpace(opt.URL)
	if url == "" {
		return nil
	}
	timeout := opt.Timeout
	if timeout <= 0 {
		timeout = 60 * time.Second
	}
	model := opt.Model
	if model == "" {
		model = "whisper-1"
	}
	return &WhisperAPI{
		url:      url,
		apikey:   strings.TrimSpace(opt.APIKey),
		model:    model,
		language: opt.Language,
		client:   &http.Client{Timeout: timeout},
	}
}

// Transcribe ses baytlarını metne çevirir.
func (w *WhisperAPI) Transcribe(ctx context.Context, audio []byte, mime string) (string, error) {
	if w == nil {
		return "", ErrNoRecognizer
	}
	if len(audio) == 0 {
		return "", ErrEmptyAudio
	}
	if mime == "" {
		mime = "application/octet-stream"
	}

	var body bytes.Buffer
	mw := multipart.NewWriter(&body)
	// Alan adı "file" OLMALI: OpenAI uyumlu sunucular isimden okur.
	part, err := mw.CreateFormFile("file", "audio"+extFor(mime))
	if err != nil {
		return "", err
	}
	if _, err := part.Write(audio); err != nil {
		return "", err
	}
	if err := mw.WriteField("model", w.model); err != nil {
		return "", err
	}
	if w.language != "" {
		if err := mw.WriteField("language", w.language); err != nil {
			return "", err
		}
	}
	if err := mw.Close(); err != nil {
		return "", err
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, w.url, &body)
	if err != nil {
		return "", err
	}
	req.Header.Set("Content-Type", mw.FormDataContentType())
	if w.apikey != "" {
		req.Header.Set("Authorization", "Bearer "+w.apikey)
	}

	resp, err := w.client.Do(req)
	if err != nil {
		return "", fmt.Errorf("%w: %v", ErrUnavailable, err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		raw, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
		return "", fmt.Errorf("%w: HTTP %d: %s", ErrUnavailable, resp.StatusCode, strings.TrimSpace(string(raw)))
	}

	// İki biçim desteklenir: {"text": "..."} (OpenAI) ve {"transcription": "..."}
	// (bazı uyumlu sunucular). İkisi de yanlışsa ham gövde metin kabul edilir.
	var out struct {
		Text           string `json:"text"`
		Transcription  string `json:"transcription"`
		SegmentsResult []struct {
			Text string `json:"text"`
		} `json:"segments"`
	}
	raw, err := io.ReadAll(io.LimitReader(resp.Body, 8<<20))
	if err != nil {
		return "", fmt.Errorf("%w: gövde okunamadı: %v", ErrUnavailable, err)
	}
	if err := json.Unmarshal(raw, &out); err != nil {
		return "", fmt.Errorf("%w: yanıt okunamadı: %v", ErrUnavailable, err)
	}
	switch {
	case out.Text != "":
		return out.Text, nil
	case out.Transcription != "":
		return out.Transcription, nil
	default:
		return "", fmt.Errorf("%w: yanıtta metin yok", ErrUnavailable)
	}
}
