// Package dao 提供 transcribe 模块对外部 LLM 配置中心的只读访问
package dao

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/echochat/backend/app/transcribe/model/llm"
	"github.com/echochat/backend/pkg/db"
)

// ErrLLMSourceDisabled LLM 配置库未启用
var ErrLLMSourceDisabled = errors.New("LLM 配置中心未启用 (llm_source.enabled=false)")

// ErrNoActiveSTT 没有可用的 STT 模型/Key
var ErrNoActiveSTT = errors.New("未找到可用的 STT 模型或 API Key")

// LLMConfigDAO 外部 LLM 配置只读 DAO（MySQL）
//
// 选择策略说明（PickActiveSTT）：
//   1. 在 t_llm_model 中找 model_type=4 (ASR) 且 status=1 deleted=0 ORDER BY sort ASC 的第一条
//   2. 用其 provider_id 在 t_llm_provider 中找对应 provider，要求 status=1 deleted=0
//   3. 在 t_llm_key 中按 provider_id 选 status=1 deleted=0 且 (expire_time IS NULL OR expire_time > NOW())
//      且 (daily_limit=0 OR today_count < daily_limit) ORDER BY weight DESC, today_count ASC LIMIT 1
//
// 任何一步落空都返回 ErrNoActiveSTT，避免上游用半成品配置发起调用。
type LLMConfigDAO struct {
	source *db.LLMSourceDB
}

// NewLLMConfigDAO 创建实例
func NewLLMConfigDAO(source *db.LLMSourceDB) *LLMConfigDAO {
	return &LLMConfigDAO{source: source}
}

// IsEnabled 暴露给上层做"是否能转写"的快速判断，避免每次都 catch error
func (d *LLMConfigDAO) IsEnabled() bool {
	return d != nil && d.source != nil && d.source.IsEnabled()
}

// STTConfig 三表合一的运行时配置快照，用于本次转写调用
type STTConfig struct {
	Provider llm.Provider
	Model    llm.Model
	Key      llm.Key
}

// PickActiveSTT 按选择策略挑出一组可用的 (provider, model, key)
//
// 注意：不做事务，因为这是只读 + LLM 配置中心通常更新频率极低，
// 偶发的"挑出后 key 立刻被禁用"由调用层捕获 401/403 后重试解决。
func (d *LLMConfigDAO) PickActiveSTT(ctx context.Context) (*STTConfig, error) {
	if !d.IsEnabled() {
		return nil, ErrLLMSourceDisabled
	}
	gdb := d.source.DB()

	// 1) 选 ASR 模型
	var model llm.Model
	err := gdb.WithContext(ctx).
		Where("deleted = 0 AND status = ? AND model_type = ?", llm.StatusEnabled, llm.ModelTypeASR).
		Order("sort ASC, id ASC").
		First(&model).Error
	if err != nil {
		return nil, fmt.Errorf("%w: %v", ErrNoActiveSTT, err)
	}

	// 2) 模型对应的 provider 必须也启用
	var provider llm.Provider
	err = gdb.WithContext(ctx).
		Where("id = ? AND deleted = 0 AND status = ?", model.ProviderID, llm.StatusEnabled).
		First(&provider).Error
	if err != nil {
		return nil, fmt.Errorf("%w: provider not active for model %s", ErrNoActiveSTT, model.ModelCode)
	}

	// 3) 选 key
	now := time.Now()
	var key llm.Key
	err = gdb.WithContext(ctx).
		Where("deleted = 0 AND status = ? AND provider_id = ?", llm.StatusEnabled, provider.ID).
		Where("expire_time IS NULL OR expire_time > ?", now).
		Where("daily_limit = 0 OR today_count < daily_limit").
		Order("weight DESC, today_count ASC, id ASC").
		First(&key).Error
	if err != nil {
		return nil, fmt.Errorf("%w: no usable key for provider %s", ErrNoActiveSTT, provider.ProviderCode)
	}

	return &STTConfig{Provider: provider, Model: model, Key: key}, nil
}

// ListASRModels 列出全部启用的 ASR 模型（admin 可视化用，可不接 UI，先备好接口）
func (d *LLMConfigDAO) ListASRModels(ctx context.Context) ([]llm.Model, error) {
	if !d.IsEnabled() {
		return nil, ErrLLMSourceDisabled
	}
	var list []llm.Model
	err := d.source.DB().WithContext(ctx).
		Where("deleted = 0 AND status = ? AND model_type = ?", llm.StatusEnabled, llm.ModelTypeASR).
		Order("sort ASC, id ASC").
		Find(&list).Error
	return list, err
}
