package controllers

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

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

// VcardApiIndex renderiza el panel de integración VCard.
func VcardApiIndex(c *fiber.Ctx) error {
	cfg, _ := models.GetVcardApiConfig()
	data := fiber.Map{
		"user":    c.Locals("user").(map[string]interface{}),
		"modules": c.Locals("userModules"),
		"config":  cfg,
	}
	return c.Render("vcard_api", data, "layouts/main")
}

// VcardApiLogin llama a /api/login del sistema VCard externo y devuelve el access_token.
func VcardApiLogin(c *fiber.Ctx) error {
	type Req struct {
		BaseURL  string `json:"base_url"`
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	var req Req
	if err := c.BodyParser(&req); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
	}
	req.BaseURL = strings.TrimRight(strings.TrimSpace(req.BaseURL), "/")
	if req.BaseURL == "" || req.Email == "" || req.Password == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "base_url, email y password son requeridos"})
	}

	payload, _ := json.Marshal(map[string]string{"email": req.Email, "password": req.Password})
	client := &http.Client{Timeout: 15 * time.Second}
	resp, err := client.Post(req.BaseURL+"/api/login", "application/json", strings.NewReader(string(payload)))
	if err != nil {
		return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": "No se pudo conectar: " + err.Error()})
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)

	var result map[string]interface{}
	if err := json.Unmarshal(body, &result); err != nil {
		return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": "Respuesta inválida del servidor"})
	}
	if token, ok := result["access_token"].(string); ok && token != "" {
		return c.JSON(fiber.Map{"ok": true, "token": token})
	}
	msg := "Credenciales incorrectas"
	if m, ok := result["message"].(string); ok && m != "" {
		msg = m
	}
	return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": msg})
}

// VcardApiSaveConfig guarda / actualiza la configuración (base URL + token).
func VcardApiSaveConfig(c *fiber.Ctx) error {
	type Req struct {
		Nombre      string `json:"nombre"`
		BaseURL     string `json:"base_url"`
		BearerToken string `json:"bearer_token"`
		Activo      bool   `json:"activo"`
	}
	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.BaseURL) == "" {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "base_url es requerido"})
	}
	cfg, err := models.UpsertVcardApiConfig(req.Nombre, strings.TrimRight(strings.TrimSpace(req.BaseURL), "/"), req.BearerToken, req.Activo)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
	}
	return c.JSON(fiber.Map{"ok": true, "config": cfg})
}

// VcardApiGetConfig devuelve la configuración actual (sin exponer el token completo).
func VcardApiGetConfig(c *fiber.Ctx) error {
	cfg, err := models.GetVcardApiConfig()
	if err != nil {
		return c.JSON(fiber.Map{"config": nil})
	}
	masked := "••••••••"
	if cfg.BearerToken == "" {
		masked = ""
	}
	return c.JSON(fiber.Map{
		"config": fiber.Map{
			"ID":           cfg.ID,
			"nombre":       cfg.Nombre,
			"base_url":     cfg.BaseURL,
			"bearer_token": masked,
			"activo":       cfg.Activo,
		},
	})
}

// vcardDo ejecuta una llamada a la API Admin VCard y devuelve el body como json.RawMessage.
func vcardDo(method, endpoint string, cfg *models.VcardApiConfig) ([]byte, int, error) {
	return vcardDoWithBody(method, endpoint, nil, "", cfg)
}

// vcardDoWithBody ejecuta una llamada a la API Admin VCard con body opcional.
func vcardDoWithBody(method, endpoint string, reqBody io.Reader, contentType string, cfg *models.VcardApiConfig) ([]byte, int, error) {
	url := fmt.Sprintf("%s%s", cfg.BaseURL, endpoint)
	client := &http.Client{Timeout: 15 * time.Second}
	req, err := http.NewRequest(method, url, reqBody)
	if err != nil {
		return nil, 0, err
	}
	req.Header.Set("Authorization", "Bearer "+cfg.BearerToken)
	req.Header.Set("Accept", "application/json")
	if contentType != "" {
		req.Header.Set("Content-Type", contentType)
	}

	resp, err := client.Do(req)
	if err != nil {
		return nil, 0, err
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(io.LimitReader(resp.Body, 512*1024))
	return body, resp.StatusCode, nil
}

// proxyVcard extrae la config, llama al endpoint y devuelve el resultado al frontend.
func proxyVcard(c *fiber.Ctx, method, endpoint string) error {
	cfg, err := models.GetVcardApiConfig()
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Configura primero la API VCard"})
	}
	if !cfg.Activo {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "La integración está inactiva"})
	}

	qs := string(c.Request().URI().QueryString())
	ep := endpoint
	if qs != "" {
		ep = endpoint + "?" + qs
	}

	body, status, err := vcardDo(method, ep, cfg)
	if err != nil {
		return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()})
	}
	var result json.RawMessage
	if err := json.Unmarshal(body, &result); err != nil {
		result = json.RawMessage(fmt.Sprintf(`{"raw": %q}`, string(body)))
	}
	c.Status(status)
	return c.JSON(result)
}

// proxyVcardMutate reenvía el body del request original a la API VCard.
func proxyVcardMutate(c *fiber.Ctx, method, endpoint string) error {
	cfg, err := models.GetVcardApiConfig()
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Configura primero la API VCard"})
	}
	if !cfg.Activo {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "La integración está inactiva"})
	}

	var bodyReader io.Reader
	ct := ""
	if raw := c.Body(); len(raw) > 0 {
		bodyReader = strings.NewReader(string(raw))
		ct = c.Get("Content-Type", "application/json")
	}

	body, status, err := vcardDoWithBody(method, endpoint, bodyReader, ct, cfg)
	if err != nil {
		return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()})
	}
	var result json.RawMessage
	if err := json.Unmarshal(body, &result); err != nil {
		result = json.RawMessage(fmt.Sprintf(`{"raw": %q}`, string(body)))
	}
	c.Status(status)
	return c.JSON(result)
}

// ─── Proxy endpoints ──────────────────────────────────────────────────────────

func VcardApiUsuarios(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios")
}

func VcardApiUsuario(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios/"+c.Params("id"))
}

func VcardApiVcards(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/vcards")
}

func VcardApiVcard(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/vcards/"+c.Params("id"))
}

func VcardApiVcardsByUsuario(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios/"+c.Params("userId")+"/vcards")
}

func VcardApiPlanes(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/planes")
}

func VcardApiPlan(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/planes/"+c.Params("id"))
}

func VcardApiPlanUpdate(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPut, "/api/admin/planes/"+c.Params("id"))
}

func VcardApiMembresia(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios/"+c.Params("id")+"/membresia")
}

func VcardApiPagos(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/pagos")
}

func VcardApiPagosByUsuario(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios/"+c.Params("userId")+"/pagos")
}

func VcardApiTransacciones(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/transacciones")
}

func VcardApiTransaccionesByUsuario(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios/"+c.Params("userId")+"/transacciones")
}

func VcardApiLogs(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/logs")
}

func VcardApiLogsByUsuario(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios/"+c.Params("userId")+"/logs")
}

func VcardApiMiniwebs(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/miniwebs")
}

func VcardApiMiniweb(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/miniwebs/"+c.Params("id"))
}

func VcardApiMiniwebsByUsuario(c *fiber.Ctx) error {
	return proxyVcard(c, http.MethodGet, "/api/admin/usuarios/"+c.Params("userId")+"/miniwebs")
}

// ─── Mutación endpoints ───────────────────────────────────────────────────────

func VcardApiUsuarioUpdate(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPut, "/api/admin/usuarios/"+c.Params("id"))
}

func VcardApiUsuarioActivar(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPost, "/api/admin/usuarios/"+c.Params("id")+"/activar")
}

func VcardApiUsuarioDesactivar(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPost, "/api/admin/usuarios/"+c.Params("id")+"/desactivar")
}

func VcardApiVcardUpdate(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPut, "/api/admin/vcards/"+c.Params("id"))
}

func VcardApiActivarMembresia(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPost, "/api/admin/usuarios/"+c.Params("id")+"/activar-membresia")
}

func VcardApiDesactivarMembresia(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPost, "/api/admin/usuarios/"+c.Params("id")+"/desactivar-membresia")
}

func VcardApiCambiarPlan(c *fiber.Ctx) error {
	return proxyVcardMutate(c, http.MethodPost, "/api/admin/usuarios/"+c.Params("id")+"/cambiar-plan")
}
