package controllers

import (
	"fmt"
	"math"
	"strconv"

	"github.com/gofiber/fiber/v2" //nolint:goimports
	"github.com/sujit-baniya/fiber-boilerplate/app"
	"github.com/sujit-baniya/fiber-boilerplate/config"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
	"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
)

// RENDER
func Users(c *fiber.Ctx) error {
	data := fiber.Map{
		"user":    c.Locals("user").(map[string]interface{}),
		"modules": c.Locals("userModules"),
	}

	// Renderiza la vista "modules" con el layout "layouts/landing"
	if err := c.Render("users", data, "layouts/main"); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error rendering template",
			"error":   err.Error(),
		})
	}
	return nil
}

// RENDER usuarios gas
func UsersGas(c *fiber.Ctx) error {
	data := fiber.Map{
		"user":    c.Locals("user").(map[string]interface{}),
		"modules": c.Locals("userModules"),
	}
	// Renderiza la vista "modules" con el layout "layouts/landing"
	if err := c.Render("users_gas", data, "layouts/main"); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error rendering template",
			"error":   err.Error(),
		})
	}
	return nil
}

// CONSULTAR TODOS LOS REGISTROS
func GetUsers(c *fiber.Ctx) error {
	// Obtener parámetros de consulta para la paginación
	pageStr := c.Query("page", "1") // Por defecto, la página es 1
	page, err := strconv.Atoi(pageStr)
	if err != nil || page < 1 {
		page = 1
	}

	searchQuery := c.Query("search", "") // Obtener el término de búsqueda
	limit := 10                          // Número de módulos por página
	offset := (page - 1) * limit

	// Llama a AllUsers con el término de búsqueda
	Users, totalUsers, err := models.AllUsersSistema(limit, offset, searchQuery)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error retrieving users",
			"error":   err.Error(),
		})
	}

	// Llama a AllRoles sin el término de búsqueda para obtener todos los roles
	roles, err := models.AllRolesSelect() // Pasamos 0 para limit y offset para obtener todos los roles
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error retrieving roles",
			"error":   err.Error(),
		})
	}

	// Calcular el total de páginas para usuarios
	totalPages := int(math.Ceil(float64(totalUsers) / float64(limit)))

	// Envía los usuarios y roles como respuesta JSON junto con la información de paginación
	return c.JSON(fiber.Map{
		"Users":      Users,
		"Roles":      roles,
		"total":      totalUsers, // Total de usuarios disponibles
		"totalPages": totalPages, // Total de páginas
		"page":       page,       // Página actual
		"limit":      limit,      // Límites por página
	})
}

// CONSULTAR TODOS LOS REGISTROS GAS
func GetusersGas(c *fiber.Ctx) error {
	// Obtener parámetros de consulta para la paginación
	pageStr := c.Query("page", "1") // Por defecto, la página es 1
	page, err := strconv.Atoi(pageStr)
	if err != nil || page < 1 {
		page = 1
	}

	searchQuery := c.Query("search", "") // Obtener el término de búsqueda
	limit := 10                          // Número de módulos por página
	offset := (page - 1) * limit

	// Llama a AllUsers con el término de búsqueda
	Users, totalUsers, err := models.AllUsersGas(limit, offset, searchQuery)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error retrieving users",
			"error":   err.Error(),
		})
	}

	// Calcular el total de páginas para usuarios
	totalPages := int(math.Ceil(float64(totalUsers) / float64(limit)))

	// Envía los usuarios y roles como respuesta JSON junto con la información de paginación
	return c.JSON(fiber.Map{
		"Users":      Users,
		"total":      totalUsers, // Total de usuarios disponibles
		"totalPages": totalPages, // Total de páginas
		"page":       page,       // Página actual
		"limit":      limit,      // Límites por página
	})
}

// GetUser consulta un solo registro de usuario por su ID
func GetUser(c *fiber.Ctx) error {
	// Obtiene el ID del parámetro de la URL
	idParam := c.Params("id")
	fmt.Print(idParam)

	// Convierte el parámetro a un uint
	userID, err := strconv.ParseUint(idParam, 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Invalid user ID",
			"error":   err.Error(),
		})
	}

	// Canal para recibir el resultado de la consulta
	resultChan := make(chan *models.Users)
	errorChan := make(chan error)

	// Ejecuta la consulta en una goroutine
	go func() {
		user, err := models.FindUserByID(uint(userID))
		if err != nil {
			errorChan <- err
			return
		}
		// Si no hay error, envía el resultado al canal
		resultChan <- user
	}()

	// Espera el resultado o el error de la goroutine
	select {
	case user := <-resultChan:
		// Si el usuario no existe
		if user == nil {
			return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
				"message": "User not found",
			})
		}
		// Envía el usuario como respuesta JSON
		return c.JSON(fiber.Map{
			"user": user,
		})
	case err := <-errorChan:
		// Maneja el error
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error retrieving user",
			"error":   err.Error(),
		})
	}
}

func UpdateUser(c *fiber.Ctx) error {
	var m models.Users
	uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "ID inválido",
			"error":   true,
		})
	}

	// Analiza el cuerpo de la solicitud
	if err := c.BodyParser(&m); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Error al analizar el cuerpo de la solicitud",
			"error":   true,
		})
	}

	m.ID = uint(uid)

	// Si el campo Estado está presente, solo se actualiza ese campo
	if m.Estado != nil {
		// Actualiza el estado del usuario
		if err := models.UpdateUserEstado(m.ID, *m.Estado); err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"message": err.Error(),
				"error":   true,
			})
		}
		return c.Status(fiber.StatusOK).JSON(fiber.Map{
			"message": "Estado del usuario actualizado con éxito",
			"error":   false,
			"user":    m,
		})
	}

	// Verifica que al menos dos de los campos relevantes estén llenos (Name, NombreUsuario, Email)
	if (m.Name == "" && m.NombreUsuario == "" && m.Email == "") ||
		(m.Name == "" && m.Email == "") ||
		(m.NombreUsuario == "" && m.Email == "") {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Se deben llenar al menos dos de los siguientes campos: Name, NombreUsuario, Email",
			"error":   true,
		})
	}

	// Crea un mapa para los campos que se actualizarán
	updates := make(map[string]interface{})

	// Agrega los campos que están presentes en la solicitud
	if m.Name != "" {
		updates["Name"] = m.Name
	}
	if m.NombreUsuario != "" {
		updates["NombreUsuario"] = m.NombreUsuario
	}
	if m.Email != "" {
		updates["email"] = m.Email
	}
	if m.RoleID != 0 {
		updates["role_id"] = m.RoleID
	}

	updates["telegram_chat_id"] = m.TelegramChatID

	// Realiza la actualización de los campos seleccionados
	if err := models.UpdateUser(m.ID, m.Name, m.NombreUsuario, m.Email, int(m.RoleID), m.TelegramChatID); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": err.Error(),
			"error":   true,
		})
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message": "Usuario actualizado con éxito",
		"error":   false,
		"user":    m,
	})
}

func UpdateUserGas(c *fiber.Ctx) error {
	var m models.Users
	uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "ID inválido",
			"error":   true,
		})
	}

	var existing models.Users
	if err := app.Http.Database.DB.First(&existing, uid).Error; err != nil {
		return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
			"message": "Registro no encontrado",
			"error":   true,
		})
	}

	if err := c.BodyParser(&m); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Error al analizar el cuerpo de la solicitud",
			"error":   true,
		})
	}

	if m.Name != "" {
		existing.Name = m.Name
	}
	if m.NombreUsuario != "" {
		existing.NombreUsuario = m.NombreUsuario
	}
	if m.NumeroMedidor != "" {
		existing.NumeroMedidor = m.NumeroMedidor
	}
	if m.Email != "" {
		existing.Email = m.Email
	}
	if m.Celular != "" {
		existing.Celular = m.Celular
	}

	if err := app.Http.Database.DB.Save(&existing).Error; err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error al guardar los cambios",
			"error":   true,
		})
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message": "Usuario actualizado con éxito",
		"error":   false,
		"user":    existing,
	})
}

// ACTUALIZAR
func UpdatePassword(c *fiber.Ctx) error {
	var m models.Users
	uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "ID inválido",
			"error":   true,
		})
	}

	// Analiza el cuerpo de la solicitud
	if err := c.BodyParser(&m); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Error al analizar el cuerpo de la solicitud",
			"error":   true,
		})
	}

	m.ID = uint(uid)

	// Verifica si hay un nuevo password para actualizar
	if m.Password != "" {
		// Crea una instancia de Hash
		hash := config.Hash{}

		// Encripta la nueva contraseña
		hashedPassword, err := hash.Create(m.Password)
		if err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"message": "Error al encriptar la contraseña",
				"error":   true,
			})
		}
		// Asigna la contraseña encriptada al modelo
		m.Password = hashedPassword

		// Actualiza solo el campo de contraseña en la base de datos
		if err := models.UpdateUserPassword(m.ID, m.Password); err != nil {
			return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"message": err.Error(),
				"error":   true,
			})
		}
	}

	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message": "Contraseña actualizada con éxito",
		"error":   false,
	})
}

// CREAR
func CreateUser(c *fiber.Ctx) error {
	var m models.Users

	// Analiza el cuerpo de la solicitud
	if err := c.BodyParser(&m); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Error al analizar el cuerpo de la solicitud",
			"error":   true,
		})
	}

	m.TipoUsuario = "sistema"
	estado := true
	m.Estado = &estado
	m.EmailVerified = true

	// Crea el nuevo módulo en la base de datos
	if err := models.CreateUser(m); err != nil { // Asegúrate de tener una función para crear un módulo
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": err.Error(),
			"error":   true,
		})
	}

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"message": "Usuario creado con éxito",
		"error":   false,
		"user":    m,
	})
}

// ELIMINAR
func DeleteUser(c *fiber.Ctx) error {
	uid, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Invalid ID",
			"error":   true,
		})
	}

	if err := models.DeleteUser(uint(uid)); err != nil { // Ensure you have a function for deleting a module
		return c.JSON(fiber.Map{
			"message": err.Error(),
			"error":   true,
		})
	}
	return c.JSON(fiber.Map{
		"message": "Usuario eliminado correctamente",
		"error":   false,
	})
}

func CreateUserGas(c *fiber.Ctx) error {
	var m models.Users

	// Analiza el cuerpo de la solicitud
	if err := c.BodyParser(&m); err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Error al analizar el cuerpo de la solicitud",
			"error":   true,
		})
	}

	// Verifica si el nombre de usuario ya existe
	exists, err := models.CheckUsernameExists(m.NombreUsuario)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error al verificar el nombre de usuario",
			"error":   err.Error(),
		})
	}

	if exists {
		return c.Status(fiber.StatusConflict).JSON(fiber.Map{
			"message": "El codigo ya se encuentra registrado",
			"error":   true,
		})
	}
	// Debug: imprimir los datos que se pasan a GetValidarMedidor
	fmt.Println("Llamando a GetValidarMedidor con NombreUsuario:", m.NombreUsuario, "y NumeroMedidor:", m.NumeroMedidor)
	// Aquí llamamos a la función GetValidarMedidor pasándole el código y medidor directamente desde el cuerpo de la solicitud

	// Obtén el RoleID basado en el RoleName
	roleName := "Usuarios"
	role, err := models.FindRoleByName(roleName)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error al obtener el rol",
			"error":   err.Error(),
		})
	}

	// Asigna el RoleID al usuario
	m.RoleID = role.ID
	m.TipoUsuario = "gas"
	estado := true
	m.Estado = &estado
	m.Password = ""

	// Crea el nuevo usuario en la base de datos (sin asignar el ID manualmente)
	if err := models.CreateUser(m); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": err.Error(),
			"error":   true,
		})
	}

	user, err := models.GetUserByUsuarioVerify(m.NombreUsuario)
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error retrieving user",
			"error":   err.Error(),
		})
	}
	// Aquí el modelo `m` debe tener el ID generado por la base de datos
	fmt.Println("Nuevo usuario creado con ID:", user.ID)

	// Llama a la función createPassword pasando el ID del nuevo usuario
	if err := createPassword(c, user.ID); err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error al actualizar la contraseña",
			"error":   true,
		})
	}

	return c.Status(fiber.StatusCreated).JSON(fiber.Map{
		"message": "Usuario creado con éxito",
		"error":   false,
		"user":    m,
	})
}

func createPassword(c *fiber.Ctx, UserId uint) error {
	// Modelo para recibir los datos del cuerpo de la solicitud
	type PasswordUpdateRequest struct {
		Password string `json:"password"`
	}

	var req PasswordUpdateRequest

	// Analiza el cuerpo de la solicitud
	fmt.Println("Recibiendo solicitud para actualizar contraseña...")
	if err := c.BodyParser(&req); err != nil {
		fmt.Println("Error al analizar el cuerpo de la solicitud:", err)
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "Error al analizar el cuerpo de la solicitud",
			"error":   true,
		})
	}

	// Verifica si el password no está vacío
	if req.Password == "" {
		fmt.Println("La contraseña no puede estar vacía")
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"message": "La contraseña no puede estar vacía",
			"error":   true,
		})
	}

	// Crea una instancia del encriptador de contraseñas
	hash := config.Hash{}

	// Encripta la nueva contraseña
	fmt.Println("Encriptando la contraseña...")
	hashedPassword, err := hash.Create(req.Password)
	if err != nil {
		fmt.Println("Error al encriptar la contraseña:", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error al encriptar la contraseña",
			"error":   true,
		})
	}
	fmt.Println("Contraseña encriptada con éxito.")

	// Actualiza solo el campo de contraseña en la base de datos
	fmt.Printf("Actualizando la contraseña del usuario con ID %d\n", UserId)
	if err := models.UpdateUserPassword(UserId, hashedPassword); err != nil {
		fmt.Println("Error al actualizar la contraseña en la base de datos:", err)
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"message": "Error al actualizar la contraseña en la base de datos",
			"error":   true,
		})
	}

	// Respuesta de éxito
	fmt.Println("Contraseña actualizada con éxito.")
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"message": "Contraseña actualizada con éxito",
		"error":   false,
	})
}

func EnviarCredencialesUsuario(c *fiber.Ctx) error {
	id, err := strconv.ParseUint(c.Params("id"), 10, 32)
	if err != nil {
		return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
	}
	user, err := models.FindUserByID(uint(id))
	if err != nil || user == nil {
		return c.Status(404).JSON(fiber.Map{"error": "Usuario no encontrado"})
	}
	if user.Email == "" {
		return c.Status(400).JSON(fiber.Map{"error": "El usuario no tiene email registrado"})
	}
	go services.SendUserCredencialesEmail(user.Email, user.Name, user.NombreUsuario)
	return c.JSON(fiber.Map{"ok": true, "message": "Credenciales enviadas a " + user.Email})
}
