package models

import (
	"fmt"
	"log"
	"strings"

	"github.com/sujit-baniya/fiber-boilerplate/app"
	"gorm.io/gorm"
)

// AiConfig almacena las configuraciones de proveedores de IA (Qwen, OpenAI, etc.)
// que son usadas por el Landing Generator y otros módulos.
type AiConfig struct {
	gorm.Model
	Nombre    string `gorm:"size:100;not null" json:"nombre"`      // Alias amigable, ej: "Qwen 2.5 Producción"
	Provider  string `gorm:"size:50;not null"  json:"provider"`    // qwen | openai | anthropic | etc
	ApiKey    string `gorm:"type:text;not null" json:"api_key"`    // Clave de API
	BaseURL   string `gorm:"type:text"          json:"base_url"`   // URL base (override), vacío = default del provider
	ModelName string `gorm:"size:100"           json:"model_name"` // ej: qwen2.5-72b-instruct
	IsActive  bool   `gorm:"default:true"       json:"is_active"`  // Solo uno activo a la vez
	Notes     string `gorm:"type:text"          json:"notes"`
	// Modulo indica a qué servicio pertenece esta config.
	// "" = global (disponible para todos como fallback)
	// "landing" = exclusivo para Landing Generator
	// "query_runner" = exclusivo para Query Runner SQL
	Modulo string `gorm:"size:50;default:''" json:"modulo"`
	// Agente Telegram: si EsAgenteBot=true, esta config es el cerebro del bot administrador.
	// Solo debe haber una config activa como agente a la vez.
	EsAgenteBot      bool  `gorm:"default:false"      json:"es_agente_bot"`
	TelegramConfigID *uint `gorm:"index"              json:"telegram_config_id"`
}

func (AiConfig) TableName() string { return "ai_configs" }

func GetAllAiConfigs(limit, offset int, search string) ([]AiConfig, int64, error) {
	var items []AiConfig
	var total int64
	db := app.Http.Database.DB.Model(&AiConfig{})
	if search != "" {
		db = db.Where("nombre LIKE ? OR provider LIKE ?", "%"+search+"%", "%"+search+"%")
	}
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}
	if err := db.Order("id DESC").Limit(limit).Offset(offset).Find(&items).Error; err != nil {
		return nil, 0, err
	}
	return items, total, nil
}

func CreateAiConfig(item *AiConfig) error {
	return app.Http.Database.DB.Create(item).Error
}

func UpdateAiConfig(id uint, updates map[string]interface{}) error {
	return app.Http.Database.DB.Model(&AiConfig{}).Where("id = ?", id).Updates(updates).Error
}

func DeleteAiConfig(id uint) error {
	return app.Http.Database.DB.Delete(&AiConfig{}, id).Error
}

func GetAiConfigByID(id uint, out *AiConfig) error {
	return app.Http.Database.DB.First(out, id).Error
}

// GetActiveAiConfig retorna la primera configuración activa del provider indicado.
// Si provider está vacío, retorna cualquier config activa.
func GetActiveAiConfig(provider string) (*AiConfig, error) {
	var item AiConfig
	db := app.Http.Database.DB.Where("is_active = ?", true)
	if provider != "" {
		db = db.Where("provider = ?", provider)
	}
	if err := db.First(&item).Error; err != nil {
		log.Printf("[AI_CONFIG] No se encontró config activa para provider '%s': %v", provider, err)
		return nil, err
	}
	return &item, nil
}

// SplitModulos parte el campo Modulo (comma-separated) en un slice limpio.
// "" → [] (config global), "landing,query_runner" → ["landing","query_runner"]
func SplitModulos(modulo string) []string {
	var out []string
	for _, s := range strings.Split(modulo, ",") {
		if s = strings.TrimSpace(s); s != "" {
			out = append(out, s)
		}
	}
	return out
}

// JoinModulos normaliza y une un slice de módulos en comma-separated.
func JoinModulos(modules []string) string {
	var clean []string
	for _, s := range modules {
		if s = strings.TrimSpace(s); s != "" {
			clean = append(clean, s)
		}
	}
	return strings.Join(clean, ",")
}

func GetAiConfigSelect() ([]AiConfig, error) {
	var items []AiConfig
	if err := app.Http.Database.DB.Model(&AiConfig{}).Select("id, nombre, provider").Order("nombre ASC").Find(&items).Error; err != nil {
		return nil, err
	}
	return items, nil
}

// GetAgenteBotConfig retorna la config marcada como agente Telegram, con su TelegramConfig cargada.
func GetAgenteBotConfig() (*AiConfig, *TelegramConfig, error) {
	var ai AiConfig
	if err := app.Http.Database.DB.Where("es_agente_bot = ? AND is_active = ?", true, true).First(&ai).Error; err != nil {
		return nil, nil, fmt.Errorf("no hay agente bot configurado: %w", err)
	}
	if ai.TelegramConfigID == nil {
		return &ai, nil, fmt.Errorf("el agente no tiene bot de Telegram asignado")
	}
	tg, err := GetTelegramConfigByID(*ai.TelegramConfigID)
	if err != nil {
		return &ai, nil, fmt.Errorf("bot de Telegram no encontrado: %w", err)
	}
	return &ai, tg, nil
}

// GetAgenteBotAiConfig retorna solo la config de IA marcada como agente (el mismo
// "cerebro" que usa el bot de Telegram), sin exigir que tenga un bot de Telegram
// asignado. La usa el chat propio del dashboard para compartir el mismo motor.
func GetAgenteBotAiConfig() (*AiConfig, error) {
	var ai AiConfig
	if err := app.Http.Database.DB.Where("es_agente_bot = ? AND is_active = ?", true, true).First(&ai).Error; err != nil {
		return nil, fmt.Errorf("no hay agente configurado: %w", err)
	}
	return &ai, nil
}

// GetAiConfigForService retorna la config activa asignada al módulo indicado.
// Lógica de prioridad:
//  1. Config activa con modulo conteniendo service (puede ser comma-separated)
//  2. Config activa con modulo == "" (global, fallback)
//  3. Cualquier config activa (último recurso)
func GetAiConfigForService(service string) (*AiConfig, error) {
	var items []AiConfig
	if err := app.Http.Database.DB.Where("is_active = ?", true).Order("id ASC").Find(&items).Error; err != nil {
		return nil, fmt.Errorf("error leyendo ai_configs: %w", err)
	}
	if len(items) == 0 {
		return nil, fmt.Errorf("no hay configs de IA activas")
	}

	// 1. Config específica para el servicio (puede tener varios módulos)
	if service != "" {
		for i := range items {
			for _, m := range SplitModulos(items[i].Modulo) {
				if m == service {
					return &items[i], nil
				}
			}
		}
	}

	// 2. Config global (modulo vacío = sin restricción de servicio)
	for i := range items {
		if strings.TrimSpace(items[i].Modulo) == "" {
			return &items[i], nil
		}
	}

	// 3. Cualquier config activa como último recurso
	log.Printf("[AI_CONFIG] No se encontró config para servicio '%s', usando cualquier activa", service)
	return &items[0], nil
}
