<?php

require_once __DIR__ . '/../database/database.php';

class productoModel {

    private PDO $db;

    public function __construct() {
        $this->db = Database::getConnection();
    }

    // LISTAR PRODUCTOS

    public function listarProductos(): array {
        
        $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock, (COALESCE(p.descuento, 0) + COALESCE(c.descuento, 0)) AS descuento_total, c.nombre AS categoria, t.nombre AS tipo
                FROM productos AS p
                    INNER JOIN categorias AS c ON (p.id_categoria = c.id)
                    INNER JOIN tipos AS t ON (p.id_tipo = t.id)
                    WHERE (p.estado IS TRUE)
                ORDER BY p.id ASC";

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // LISTAR PRODUCTOS INACTIVOS

    public function listarProductosInactivos(): array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.precio, c.nombre AS categoria, t.nombre AS tipo
                FROM productos AS p
                    INNER JOIN categorias AS c ON (p.id_categoria = c.id)
                    INNER JOIN tipos AS t ON (p.id_tipo = t.id)
                    WHERE (p.estado IS FALSE)
                ORDER BY p.id ASC";

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // LISTAR PRODUCTOS PARA VENTA

    public function listarProductosVenta(): array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.precio, CAST(p.precio - ((COALESCE(p.descuento, 0) + COALESCE(c.descuento, 0)) * p.precio / 100) AS DECIMAL(10,2)) AS precio_final, p.stock,
                (COALESCE(p.descuento, 0) + COALESCE(c.descuento, 0)) AS descuento_total
                FROM productos AS p
                    INNER JOIN categorias AS c ON (p.id_categoria = c.id)
                    INNER JOIN tipos AS t ON (p.id_tipo = t.id)
                    WHERE (p.estado IS TRUE) AND (p.id_tipo = 1) AND (p.precio IS NOT NULL) AND (p.stock <> 0)
                ORDER BY p.id ASC";

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // LISTAR PORDUCTOS PARA PEDIDO

    public function listarProductosPedido(): array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.stock FROM productos AS p
                    INNER JOIN tipos AS t ON (p.id_tipo = t.id)
                    WHERE (p.estado IS TRUE)
                ORDER BY p.id ASC"; 

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // LISTAR INSUMOS PARA SERVICIO

    public function listarInsumosServicio(): array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock
                FROM productos AS p
                    INNER JOIN categorias AS c ON (p.id_categoria = c.id)
                    INNER JOIN tipos AS t ON (p.id_tipo = t.id)
                    WHERE (p.estado IS TRUE) AND (p.id_tipo = 2) AND (p.precio IS NOT NULL) AND (p.stock <> 0)
                ORDER BY p.id ASC";

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // CREAR PRODUCTO

    public function crearProducto(array $data): bool {

        $sql = "INSERT INTO productos (marca, modelo, precio, stock, descuento, estado, id_categoria, id_tipo) 
                VALUES (:marca, :modelo, :precio, :stock, :descuento, :estado, :id_categoria, :id_tipo)";

        $stmt = $this->db->prepare($sql);

        return $stmt->execute([
            ':marca' => $data['marca'],
            ':modelo' => $data['modelo'],
            ':precio' => $data['precio'],
            ':stock' => $data['stock'],
            ':descuento' => $data['descuento'],
            ':estado' => 1, // Cuando un producto se crea, automáticamente se setea el estado en true, es decir está activo
            ':id_categoria' => $data['id_categoria'],
            ':id_tipo' => $data['id_tipo']
        ]);
    }

    // OBTENER PRODUCTO MEDIANTE ID

    public function obtenerPorId(int $id): ?array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock, p.descuento, p.id_categoria, p.id_tipo, pd.id_pedido AS id_pedido FROM productos AS p
                    LEFT JOIN pedido_detalle AS pd ON (p.id = pd.id_producto)
                    WHERE p.id = :id
                ORDER BY pd.id_pedido DESC LIMIT 1";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':id' => $id]);

        $producto = $stmt->fetch(PDO::FETCH_ASSOC);

        return $producto ?: null;
    }

    // OBTENER PRODUCTO POR ID PARA CAJA

    public function obtenerPorIdParaCaja(int $id): ?array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.precio, COALESCE(p.descuento, 0) AS descuento_producto, COALESCE(c.descuento, 0) AS descuento_categoria, p.stock 
                FROM productos AS p
                    LEFT JOIN categorias AS c ON (c.id = p.id_categoria)
                WHERE (p.id = :id) AND (p.estado IS TRUE) AND (p.id_tipo = 1) LIMIT 1";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':id' => $id]);

        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }

    // OBTENER PRODUCTOS DE PEDIDO POR ID PARA CAJA

    public function obtenerPorIdParaCajaPedido(int $id): ?array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.stock, COALESCE(c.porcentaje_ganancia, 0) AS porcentaje_ganancia 
                    FROM productos AS p
                    LEFT JOIN categorias AS c ON (c.id = p.id_categoria)
                WHERE (p.id = :id) AND (p.estado IS TRUE) LIMIT 1";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':id' => $id]);

        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }

    // OBTENER INSUMO DE SERVICIO POR ID PARA CAJA

    public function obtenerPorIdParaCajaServicio(int $id): ?array {

        $sql = "SELECT p.id, p.marca, p.modelo, p.precio, p.stock 
                FROM productos AS p
                    LEFT JOIN categorias AS c ON (c.id = p.id_categoria)
                WHERE (p.id = :id) AND (p.estado IS TRUE) AND (p.id_tipo = 2) LIMIT 1";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':id' => $id]);

        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }

    // EDITAR PRODUCTO

    public function actualizarProducto(int $id, array $data): bool {

        if (empty($data)) {
            return false;
        }

        $campos = [];
        $params = [];

        foreach ($data as $campo => $valor) {
            $campos[] = "$campo = :$campo";
            $params[":$campo"] = $valor;
        }

        $params[':id'] = $id;

        $sql = "UPDATE productos SET " . implode(', ', $campos) . " WHERE id = :id";

        $stmt = $this->db->prepare($sql);

        return $stmt->execute($params);
    }

    // ACTUALIZAR ESTADO (ACTIVO / INACTIVO)

    public function actualizarEstado(int $idProducto, bool $nuevoEstado): bool {
        
        try {
            $this->db->beginTransaction();

            // Actualizamaos datos del producto

            $sql1 = "UPDATE productos 
                        SET estado = :estado,
                            precio = NULL,
                            stock = NULL,
                            descuento = NULL
                    WHERE id = :id";

            $stmt1 = $this->db->prepare($sql1);

            $stmt1->bindValue(':estado', $nuevoEstado, PDO::PARAM_BOOL);

            $stmt1->bindValue(':id', $idProducto, PDO::PARAM_INT);

            $stmt1->execute();

            // Actualizamos los stock y cantidades de pedido_detalle

            $sql2 = "UPDATE pedido_detalle
                        SET cantidad_vendida = cantidad
                    WHERE id_producto = :id";

            $stmt2 = $this->db->prepare($sql2);

            $stmt2->bindValue(':id', $idProducto, PDO::PARAM_INT);

            $stmt2->execute();

            $this->db->commit();
            return true;

        } catch (Exception $e) {
            $this->db->rollBack();

            // Log interno

            error_log($e->getMessage());

            return false;
        }
    }

    // LISTAR CATEGORIAS

    public function listarCategorias(): array {

        $sql = "SELECT id, nombre, porcentaje_ganancia, descuento FROM categorias ORDER BY nombre ASC";

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // VERIFICAR SI EXISTE UNA CATEGORIA

    public function existeCategoria(int $id): bool {
        
        $sql = "SELECT 1 FROM categorias WHERE id = :id LIMIT 1";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':id' => $id]);

        return (bool) $stmt->fetchColumn();
    }

    // OBTENER CATEGORIA POR ID

    public function obtenerPorIdCategoria(int $id): ?array {

        $sql = "SELECT id, nombre, porcentaje_ganancia, descuento FROM categorias WHERE id = :id";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':id' => $id]);

        $categoria = $stmt->fetch(PDO::FETCH_ASSOC);

        return $categoria ?: null;
    }

    // CREAR CATEGORIA

    public function crearCategoria(array $data): bool {

        $sql = "INSERT INTO categorias (nombre, porcentaje_ganancia, descuento) VALUES (:nombre, :porcentaje_ganancia, :descuento)";

        $stmt = $this->db->prepare($sql);

        return $stmt->execute([
            ':nombre' => $data['nombre'],
            ':porcentaje_ganancia' => $data['porcentaje_ganancia'],
            ':descuento' => $data['descuento']
        ]);
    }

    // EDITAR CATEGORIA

    public function actualizarCategoria(int $id, array $data): bool {

        if (empty($data)) {
            return false;
        }

        $campos = [];
        $params = [];

        foreach ($data as $campo => $valor) {
            $campos[] = "$campo = :$campo";
            $params[":$campo"] = $valor;
        }

        $params[':id'] = $id;

        $sql = "UPDATE categorias SET " . implode(', ', $campos) . " WHERE id = :id";

        $stmt = $this->db->prepare($sql);

        return $stmt->execute($params);
    }

    // ACTUALIZAR PRECIOS DE PRODUCTOS AL EDITAR LA CATEGORIA

    public function actualizarPreciosPorCategoria(int $idCategoria, array $data, array $costosProductos): bool {
       
        try {

            $this->db->beginTransaction();

            // Actualizamos la categoría

            $ok = $this->actualizarCategoria($idCategoria, $data);

            if (!$ok) {
                $this->db->rollBack();
                return false;
            }

            // Obtenemos el nuevo porcentaje de ganancia

            $stmt = $this->db->prepare("
                SELECT porcentaje_ganancia 
                FROM categorias 
                WHERE id = :id
            ");

            $stmt->execute([':id' => $idCategoria]);

            $categoria = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$categoria) {
                $this->db->rollBack();
                return false;
            }

            $porcentaje_ganancia = (float) $categoria['porcentaje_ganancia'];

            $stmt = $this->db->prepare("
                UPDATE productos 
                    SET precio = :precio 
                WHERE (id = :id) AND (id_categoria = :idCategoria)
            ");

            foreach ($costosProductos as $c) {

                // Validaciones defensivas

                if (empty($c['id_producto']) || $c['costo'] === null) {
                    continue;
                }

                $costo = (float) $c['costo'];

                // Fórmula correcta

                $nuevoPrecio = (100 + $porcentaje_ganancia) * $costo / 100;

                $stmt->execute([
                    ':precio' => $nuevoPrecio,
                    ':id' => $c['id_producto'],
                    ':idCategoria' => $idCategoria
                ]);
            }

            $this->db->commit();
            return true;

        } catch (Exception $e) {
            $this->db->rollBack();

            // Log interno

            error_log($e->getMessage());

            return false;
        }
    }

    // LISTAR TIPOS DE PRODUCTOS

    public function listarTipos(): array {

        $sql = "SELECT id, nombre FROM tipos ORDER BY id ASC";

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // VERIFICAR SI EXISTE EL TIPO DE PRODUCTO

    public function existeTipo(int $id): bool {

        $sql = "SELECT 1 FROM tipos WHERE id = :id LIMIT 1";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':id' => $id]);

        return (bool) $stmt->fetchColumn();
    }

    // OBTENER TIPO POR ID

    public function obtenerPorIdTipo(int $tipo): ?array {
        
        $sql = "SELECT id, nombre FROM tipos WHERE id = :id";

        $stmt = $this->db->prepare($sql);

        $tipo = $stmt->execute();

        return $tipo ?: null;
    }

    // OBTENER DESCUENTOS DE PRODUCTOS EN BASE A UNA CATEGORIA

    public function obtenerDescuento(int $idCategoria): ?array {

        $sql = "SELECT productos.id, productos.descuento FROM productos
                    INNER JOIN categorias ON (categorias.id = productos.id_categoria)
                WHERE (categorias.id = :idCategoria)";

        $stmt = $this->db->prepare($sql);

        $stmt->execute([':idCategoria' => $idCategoria]);

        $resultado = $stmt->fetchAll(PDO::FETCH_ASSOC);

        return !empty($resultado) ? $resultado : null;
    }

    // BÚSQUEDA EN INPUT DE DESCRIPCIÓN DEL EQUIPO (MÓDULO SERVICIOS)

    public function busquedaParaServicio(): array {

        $sql = "SELECT marca, modelo FROM productos WHERE (id_tipo = 1)";

        $stmt = $this->db->prepare($sql);

        $stmt->execute();

        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}