<?php

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

class pedidoModel {

    private PDO $db;
    private ?string $error = null;

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

    // MÉTODO PARA OBTENER ERRORES E INFORMARLOS A TRAVÉZ DEL CONTROLLER

    public function getError(): ?string {
        return $this->error;
    }

    // GUARDAR PEDIDO (retorna false o el ID del pedido generado)

    public function guardarPedido(array $productosFinales, array $proveedor, array $vendedor, float $total): int|false {
        
        // Validaciones (ya verifica el controller también)

        if (empty($productosFinales)) {
            $this->error = 'El pedido no tiene productos.';
            return false;
        }

        if (empty($proveedor['id'])) {
            $this->error = 'Proveedor inválido.';
            return false;
        }

        if (empty($vendedor['id'])) {
            $this->error = 'No se pudo vincular al usuario.';
            return false;
        }

        if ($total <= 0) {
            $this->error = 'Total de pedido inválido.';
            return false;
        }

        foreach ($productosFinales as $p) {
            if (empty($p['id_producto']) || empty($p['cantidad']) || empty($p['costo']) || empty($p['precio'])) {
                $this->error = 'Producto inválido en el pedido.';
                return false;
            }

            if ($p['cantidad'] <= 0 || $p['costo'] < 0) {
                $this->error = 'Cantidad o costo inválido.';
                return false;
            }
        }

        try {
            $this->db->beginTransaction();

            // 1. INSERT PEDIDO
            
            $sqlPedido = "
                INSERT INTO pedidos (total, id_proveedor, id_usuario)
                VALUES (:total, :id_proveedor, :id_usuario)
                RETURNING id
            ";

            $stmtPedido = $this->db->prepare($sqlPedido);

            $stmtPedido->execute([
                ':total' => $total,
                ':id_proveedor' => $proveedor['id'],
                ':id_usuario' => $vendedor['id']
            ]);

            $idPedido = $stmtPedido->fetchColumn();

            if (!$idPedido) {
                $this->error = 'No se pudo crear el pedido.';
                $this->db->rollBack();
                return false;
            }

            // 2. INSERT DETALLE (LOOP)
            
            $sqlDetalle = "
                INSERT INTO pedido_detalle
                    (precio_unitario, cantidad, subtotal, id_pedido, id_producto)
                VALUES
                    (:precio_unitario, :cantidad, :subtotal, :id_pedido, :id_producto)
            ";

            $stmtDetalle = $this->db->prepare($sqlDetalle);

            foreach ($productosFinales as $p) {

                $subtotal = $p['costo'] * $p['cantidad'];

                if (!$stmtDetalle->execute([
                        ':precio_unitario' => $p['costo'],
                        ':cantidad' => $p['cantidad'],
                        ':subtotal' => $subtotal,
                        ':id_pedido' => $idPedido,
                        ':id_producto' => $p['id_producto'],
                    ])
                ) {
                    $this->error = 'Error al guardar el detalle del pedido.';
                    $this->db->rollBack();
                    return false;
                }
            }

            // 3. ACTUALIZAR DATOS DE PRODUCTO

            $sqlStock = "
                UPDATE productos
                SET 
                    stock = COALESCE(stock, 0) + :cantidad,
                    precio = :precio
                WHERE id = :id_producto
            ";

            $stmtStock = $this->db->prepare($sqlStock);

            foreach ($productosFinales as $p) {

                if (!$stmtStock->execute([
                        ':cantidad' => $p['cantidad'],
                        ':precio' => $p['precio'],
                        ':id_producto' => $p['id_producto'],
                    ])
                ) {
                    $this->error = 'Error al actualizar el producto.';
                    $this->db->rollBack();
                    return false;
                }
            }

            // 4. COMMIT

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

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

            // Log interno

            error_log($e->getMessage());

            $this->error = 'Error interno al guardar el pedido.';
            return false;
        }
    }

    // LISTAR PEDIDOS

    public function listarPedidos(int $anio): ?array {

        $sql = "SELECT p.id, p.fecha, p.total, pe.razon_social, u.nombre AS vendedor_nombre, u.apellido AS vendedor_apellido FROM pedidos AS p
                    INNER JOIN proveedores AS pe ON (p.id_proveedor = pe.id)
                    INNER JOIN usuarios AS u ON (p.id_usuario = u.id)
                    WHERE EXTRACT(YEAR FROM p.fecha) = :anio
                ORDER BY p.id ASC";

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

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

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

    // OBTENER DETALLE DE PEDIDO

    public function getDetallePedido(int $idPedido): ?array {

        $sql = "SELECT p.id AS id_producto, p.marca, p.modelo, pd.precio_unitario, pd.cantidad, pd.subtotal FROM pedido_detalle AS pd
                    INNER JOIN productos AS p ON (pd.id_producto = p.id)
                    WHERE (pd.id_pedido = :id_pedido) 
                ORDER BY p.id ASC";

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

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

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

    // OBTENER PREVIEW DE CODIGO DE REMITO (no se utiliza más)

    /*

    public function getNumeroRemitoPreview(): int {

        $sql = "SELECT nextval('remito_preview_seq')";
        $stmt = $this->db->prepare($sql);
        $stmt->execute();

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

    */

    // OBTENER COSTO DE PRODUCTO
    
    public function obtenerCosto(int $idProducto): ?float {

        $sql = "SELECT pd.precio_unitario FROM pedido_detalle AS pd
                    INNER JOIN productos AS p ON (pd.id_producto = p.id)
                WHERE (p.id = :idProducto)
                AND pd.id_pedido = (
                    SELECT MAX(pd2.id_pedido) 
                    FROM pedido_detalle AS pd2
                    WHERE pd2.id_producto = :idProducto2
                )";

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

        $stmt->execute([
            ':idProducto'  => $idProducto,
            ':idProducto2' => $idProducto
        ]);

        $resultado = $stmt->fetchColumn();

        return $resultado !== false ? (float) $resultado : null;
    }

    // OBTENER COSTOS DE PRODUCTOS EN BASE A UNA CATEGORIA

    public function obtenerCostosPorCategoria(int $idCategoria): array {
        
        // Uso de LATERAL en LEFT JOIN -> permite traer el costo mas reciente (actúa como un foreach, ejecutando la subconsulta por cada producto)

        $sql = "SELECT 
                p.id AS id_producto, pd.precio_unitario AS costo FROM productos AS p

                LEFT JOIN LATERAL (
                    SELECT pd.precio_unitario FROM pedido_detalle AS pd
                        INNER JOIN pedidos AS pe ON (pe.id = pd.id_pedido)
                        WHERE (pd.id_producto = p.id)
                    ORDER BY pe.fecha DESC, pd.id DESC LIMIT 1
                ) pd ON true

            WHERE (p.id_categoria = :idCategoria)";

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

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

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