package stt

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"go.uber.org/zap"
)

// MiMoService 基于 Xiaomi MiMo ASR HTTP API 的语音识别实现。
// 接口兼容 OpenAI chat/completions 格式，音频仅支持 mp3/wav。
type MiMoService struct {
	apiKey   string
	model    string
	endpoint string
	timeout  time.Duration
	logger   *zap.SugaredLogger
}

// NewMiMoService 创建 MiMo STT 服务。
// model、endpoint 由 config 层保证非空，timeoutSec 为 0 时默认 10 秒。
func NewMiMoService(apiKey, model, endpoint string, timeoutSec int, logger *zap.SugaredLogger) *MiMoService {
	timeout := time.Duration(timeoutSec) * time.Second
	if timeout <= 0 {
		timeout = 10 * time.Second
	}
	return &MiMoService{
		apiKey:   apiKey,
		model:    model,
		endpoint: endpoint,
		timeout:  timeout,
		logger:   logger,
	}
}

// mimoRequest MiMo ASR 请求体。
type mimoRequest struct {
	Model      string         `json:"model"`
	Messages   []mimoMessage  `json:"messages"`
	ASROptions *mimoASROptions `json:"asr_options,omitempty"`
}

type mimoMessage struct {
	Role    string        `json:"role"`
	Content []mimoContent `json:"content"`
}

type mimoContent struct {
	Type       string       `json:"type"`
	InputAudio *mimoAudioIn `json:"input_audio,omitempty"`
}

type mimoAudioIn struct {
	Data string `json:"data"` // data URL: data:{mime};base64,{data}
}

type mimoASROptions struct {
	Language string `json:"language"`
}

// mimoResponse MiMo ASR 非流式响应。
type mimoResponse struct {
	Choices []struct {
		Message struct {
			Content string `json:"content"`
		} `json:"message"`
	} `json:"choices"`
}

// Recognize 实现 stt.Service。将音频发送到 MiMo ASR API，返回识别文本。
func (m *MiMoService) Recognize(ctx context.Context, audio []byte, opts Options) (string, error) {
	if len(audio) == 0 {
		return "", fmt.Errorf("stt: empty audio")
	}

	// MiMo 仅支持 mp3/wav，若输入为原始 PCM 则封装为 WAV
	audioData := audio
	mimeType := "audio/wav"
	if !isWAV(audio) && !isMP3(audio) {
		wav, err := pcmToWAV(audio, opts.SampleRate, 1)
		if err != nil {
			return "", fmt.Errorf("stt: pcm to wav: %w", err)
		}
		audioData = wav
	} else if isMP3(audio) {
		mimeType = "audio/mpeg"
	}

	b64 := base64.StdEncoding.EncodeToString(audioData)
	dataURL := fmt.Sprintf("data:%s;base64,%s", mimeType, b64)

	// 映射语言代码
	language := mapLanguage(opts.Language)

	reqBody := mimoRequest{
		Model: m.model,
		Messages: []mimoMessage{
			{
				Role: "user",
				Content: []mimoContent{
					{
						Type: "input_audio",
						InputAudio: &mimoAudioIn{
							Data: dataURL,
						},
					},
				},
			},
		},
	}
	if language != "" {
		reqBody.ASROptions = &mimoASROptions{Language: language}
	}

	body, err := json.Marshal(reqBody)
	if err != nil {
		return "", fmt.Errorf("stt: marshal request: %w", err)
	}

	url := strings.TrimRight(m.endpoint, "/") + "/chat/completions"

	ctx, cancel := context.WithTimeout(ctx, m.timeout)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
	if err != nil {
		return "", fmt.Errorf("stt: create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+m.apiKey)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", fmt.Errorf("stt: request mimo: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("stt: read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("stt: mimo returned %d: %s", resp.StatusCode, string(respBody))
	}

	var mResp mimoResponse
	if err := json.Unmarshal(respBody, &mResp); err != nil {
		return "", fmt.Errorf("stt: unmarshal response: %w", err)
	}

	if len(mResp.Choices) == 0 {
		// MiMo 返回空结果，视为无法识别（非错误），返回空文本
		return "", nil
	}

	text := strings.TrimSpace(mResp.Choices[0].Message.Content)
	return text, nil
}

// mapLanguage 将标准语言代码映射为 MiMo 支持的值（auto/zh/en）。
func mapLanguage(lang string) string {
	switch {
	case lang == "":
		return "auto"
	case strings.HasPrefix(lang, "zh"):
		return "zh"
	case strings.HasPrefix(lang, "en"):
		return "en"
	default:
		return "auto"
	}
}

// isWAV 检查数据是否为 WAV 格式（RIFF 头）。
func isWAV(data []byte) bool {
	return len(data) > 4 && string(data[:4]) == "RIFF"
}

// isMP3 检查数据是否为 MP3 格式（ID3 标签或帧同步字）。
func isMP3(data []byte) bool {
	if len(data) > 3 && string(data[:3]) == "ID3" {
		return true
	}
	// 帧同步字：0xFF 0xFB/0xF3/0xF2
	return len(data) > 2 && data[0] == 0xFF && (data[1]&0xE0) == 0xE0
}

// pcmToWAV 将原始 PCM 数据封装为 WAV 文件。
func pcmToWAV(pcm []byte, sampleRate, channels int) ([]byte, error) {
	if sampleRate == 0 {
		sampleRate = 16000
	}
	if channels == 0 {
		channels = 1
	}

	bitsPerSample := 16
	byteRate := sampleRate * channels * bitsPerSample / 8
	blockAlign := channels * bitsPerSample / 8
	dataSize := len(pcm)

	var buf bytes.Buffer

	// RIFF header
	buf.WriteString("RIFF")
	binary.Write(&buf, binary.LittleEndian, uint32(36+dataSize))
	buf.WriteString("WAVE")

	// fmt 子块
	buf.WriteString("fmt ")
	binary.Write(&buf, binary.LittleEndian, uint32(16))                // 子块大小
	binary.Write(&buf, binary.LittleEndian, uint16(1))                 // PCM 格式
	binary.Write(&buf, binary.LittleEndian, uint16(channels))          // 通道数
	binary.Write(&buf, binary.LittleEndian, uint32(sampleRate))        // 采样率
	binary.Write(&buf, binary.LittleEndian, uint32(byteRate))          // 字节率
	binary.Write(&buf, binary.LittleEndian, uint16(blockAlign))        // 块对齐
	binary.Write(&buf, binary.LittleEndian, uint16(bitsPerSample))     // 每样本位数

	// data 子块
	buf.WriteString("data")
	binary.Write(&buf, binary.LittleEndian, uint32(dataSize))
	buf.Write(pcm)

	return buf.Bytes(), nil
}
