package controllers

import (
	"encoding/json"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)

// AiConfigIndex renderiza la vista del panel de configuraciones de IA.
func AiConfigIndex(c *fiber.Ctx) error {
	data := fiber.Map{
		"user":    c.Locals("user").(map[string]interface{}),
		"modules": c.Locals("userModules"),
	}
	return c.Render("ai_config", data, "layouts/main")
}

// GetAiConfigs devuelve la lista paginada en JSON.
func GetAiConfigs(c *fiber.Ctx) error {
	page, _ := strconv.Atoi(c.Query("page", "1"))
	if page < 1 {
		page = 1
	}
	limit := 20
	offset := (page - 1) * limit
	search := c.Query("search", "")

	items, total, err := models.GetAllAiConfigs(limit, offset, search)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}

	// Ocultar la API key en el listado (mostrar solo últimos 4 chars)
	type safe struct {
		ID               uint   `json:"ID"`
		Nombre           string `json:"nombre"`
		Provider         string `json:"provider"`
		ApiKeyHint       string `json:"api_key_hint"`
		BaseURL          string `json:"base_url"`
		ModelName        string `json:"model_name"`
		IsActive         bool   `json:"is_active"`
		Notes            string `json:"notes"`
		Modulo           string `json:"modulo"`
		EsAgenteBot      bool   `json:"es_agente_bot"`
		TelegramConfigID *uint  `json:"telegram_config_id"`
	}
	safeItems := make([]safe, len(items))
	for i, it := range items {
		hint := "••••"
		if len(it.ApiKey) > 4 {
			hint = "••••" + it.ApiKey[len(it.ApiKey)-4:]
		}
		safeItems[i] = safe{
			ID:               it.ID,
			Nombre:           it.Nombre,
			Provider:         it.Provider,
			ApiKeyHint:       hint,
			BaseURL:          it.BaseURL,
			ModelName:        it.ModelName,
			IsActive:         it.IsActive,
			Notes:            it.Notes,
			Modulo:           it.Modulo,
			EsAgenteBot:      it.EsAgenteBot,
			TelegramConfigID: it.TelegramConfigID,
		}
	}

	return c.JSON(fiber.Map{
		"items":      safeItems,
		"total":      total,
		"totalPages": int(math.Ceil(float64(total) / float64(limit))),
		"page":       page,
	})
}

// CreateAiConfigHandler crea una nueva configuración de IA.
func CreateAiConfigHandler(c *fiber.Ctx) error {
	type Req struct {
		Nombre           string `json:"nombre"`
		Provider         string `json:"provider"`
		ApiKey           string `json:"api_key"`
		BaseURL          string `json:"base_url"`
		ModelName        string `json:"model_name"`
		IsActive         bool   `json:"is_active"`
		Notes            string `json:"notes"`
		Modulo           string `json:"modulo"`
		EsAgenteBot      bool   `json:"es_agente_bot"`
		TelegramConfigID *uint  `json:"telegram_config_id"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	if strings.TrimSpace(req.Nombre) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "nombre es requerido"})
	}
	if strings.TrimSpace(req.Provider) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "provider es requerido"})
	}
	if strings.TrimSpace(req.ApiKey) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "api_key es requerido"})
	}

	item := models.AiConfig{
		Nombre:           strings.TrimSpace(req.Nombre),
		Provider:         strings.ToLower(strings.TrimSpace(req.Provider)),
		ApiKey:           strings.TrimSpace(req.ApiKey),
		BaseURL:          strings.TrimSpace(req.BaseURL),
		ModelName:        strings.TrimSpace(req.ModelName),
		IsActive:         req.IsActive,
		Notes:            req.Notes,
		Modulo:           models.JoinModulos(strings.Split(req.Modulo, ",")),
		EsAgenteBot:      req.EsAgenteBot,
		TelegramConfigID: req.TelegramConfigID,
	}
	if err := models.CreateAiConfig(&item); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true, "id": item.ID})
}

// UpdateAiConfigHandler actualiza una configuración de IA existente.
func UpdateAiConfigHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}

	type Req struct {
		Nombre           string `json:"nombre"`
		Provider         string `json:"provider"`
		ApiKey           string `json:"api_key"` // vacío = no cambiar
		BaseURL          string `json:"base_url"`
		ModelName        string `json:"model_name"`
		IsActive         bool   `json:"is_active"`
		Notes            string `json:"notes"`
		Modulo           string `json:"modulo"`
		EsAgenteBot      bool   `json:"es_agente_bot"`
		TelegramConfigID *uint  `json:"telegram_config_id"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}

	updates := map[string]interface{}{
		"nombre":             strings.TrimSpace(req.Nombre),
		"provider":           strings.ToLower(strings.TrimSpace(req.Provider)),
		"base_url":           strings.TrimSpace(req.BaseURL),
		"model_name":         strings.TrimSpace(req.ModelName),
		"is_active":          req.IsActive,
		"notes":              req.Notes,
		"modulo":             models.JoinModulos(strings.Split(req.Modulo, ",")),
		"es_agente_bot":      req.EsAgenteBot,
		"telegram_config_id": req.TelegramConfigID,
	}
	if strings.TrimSpace(req.ApiKey) != "" {
		updates["api_key"] = strings.TrimSpace(req.ApiKey)
	}

	if err := models.UpdateAiConfig(uint(id), updates); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}

// GetAiConfigSelect devuelve lista simple para selects
func GetAiConfigSelect(c *fiber.Ctx) error {
	items, err := models.GetAiConfigSelect()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"registros": items})
}

// TestAiConfigHandler verifica conectividad con el provider de la config.
func TestAiConfigHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	var item models.AiConfig
	if err := models.GetAiConfigByID(uint(id), &item); err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "config no encontrada"})
	}

	client := &http.Client{Timeout: 10 * time.Second}

	var testURL string
	var req *http.Request

	switch item.Provider {
	case "ollama":
		base := strings.TrimSuffix(strings.TrimRight(item.BaseURL, "/"), "/v1")
		testURL = base + "/api/tags"
		req, _ = http.NewRequest("GET", testURL, nil)
		if item.ApiKey != "" && item.ApiKey != "ollama" {
			req.SetBasicAuth("ollama", item.ApiKey)
		}
	case "anthropic":
		// Anthropic no tiene /models; usamos /v1/models igual (devuelve 200 con lista)
		base := item.BaseURL
		if base == "" {
			base = "https://api.anthropic.com/v1"
		}
		testURL = base + "/models"
		req, _ = http.NewRequest("GET", testURL, nil)
		req.Header.Set("x-api-key", item.ApiKey)
		req.Header.Set("anthropic-version", "2023-06-01")
	default:
		base := item.BaseURL
		if base == "" {
			switch item.Provider {
			case "openai":
				base = "https://api.openai.com/v1"
			case "qwen":
				base = "https://dashscope.aliyuncs.com/compatible-mode/v1"
			case "groq":
				base = "https://api.groq.com/openai/v1"
			default:
				return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "BaseURL requerida para este provider"})
			}
		}
		testURL = base + "/models"
		req, _ = http.NewRequest("GET", testURL, nil)
		req.Header.Set("Authorization", "Bearer "+item.ApiKey)
	}

	resp, err := client.Do(req)
	if err != nil {
		return c.JSON(fiber.Map{"ok": false, "error": err.Error()})
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		var body map[string]interface{}
		json.NewDecoder(resp.Body).Decode(&body)
		return c.JSON(fiber.Map{"ok": false, "status": resp.StatusCode, "error": body})
	}

	// Para Ollama devolvemos la lista de modelos disponibles
	if item.Provider == "ollama" {
		var body map[string]interface{}
		json.NewDecoder(resp.Body).Decode(&body)
		return c.JSON(fiber.Map{"ok": true, "status": resp.StatusCode, "data": body})
	}

	return c.JSON(fiber.Map{"ok": true, "status": resp.StatusCode})
}

// DeleteAiConfigHandler elimina una configuración de IA.
func DeleteAiConfigHandler(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 64)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
	}
	if err := models.DeleteAiConfig(uint(id)); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true})
}
