<?php

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

class ventaModel {

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

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

    // MÉTODO PARA OBTENER ERRORES Y MANEJARLOS A TRAVÉZ DEL CONTROLLER

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

    // CALCULAR TOTAL PROVISORIO APLICANDO DESCUENTO Y RECARGO EN BASE A TIPO Y FORMA DE PAGO

    public function actualizarDatosProvisorio($descuentoPresupuesto, $recargoPresupuesto, $subtotal, $descuento): ?array {

        // El descuento propio de la venta se calcula en base al subtotal con los descuento de productos aplicados

        $descuentoProvisorio = ($descuentoPresupuesto * ($subtotal - $descuento) / 100) + $descuento; // Sumamos los dos descuentos

        // Calculo el recargo teniendo en cuenta el subtotal (descuentos de productos ya aplicados)

        $recargoProvisorio = ($recargoPresupuesto * ($subtotal - $descuento) / 100);

        $totalProvisorio = $subtotal + $recargoProvisorio - $descuentoProvisorio;
        
        return $_SESSION['prov_venta'] = [
            'recargo' => $recargoProvisorio,
            'descuento' => $descuentoProvisorio,
            'total' => $totalProvisorio
        ];
    }

    // FUNCION DE GUARDADO DE VENTA

    public function guardarVenta(array $productosFinales, array $vendedor, array $cliente, array $datosExtraVenta, array $datosProvisorios): int|false {

        // Validaciones (ya verifica el controller también)

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

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

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

        if ($datosExtraVenta['descuento'] < 0 || $datosExtraVenta['descuento'] > 100) {
            $this->error = 'El descuento debe estar entre 0 y 100%';
            return false;
        }

        if ($datosExtraVenta['recargo'] < 0 || $datosExtraVenta['recargo'] > 100) {
            $this->error = 'El recargo debe estar entre 0 y 100%';
            return false;
        }

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

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

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

            if ($p['stock'] < 0) {
                $this->error = 'Ocurrió un error en el stock.';
                return false;
            }
        }

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

            // 1. INSERT VENTA
            
            $sqlVenta = "INSERT INTO ventas (total, forma_pago, tipo_pago, descuento_pct, recargo_pct, id_cliente, id_usuario)
                VALUES (:total, :forma_pago, :tipo_pago, :descuento_pct, :recargo_pct, :id_cliente, :id_usuario)
                RETURNING id
            ";

            $stmtVenta = $this->db->prepare($sqlVenta);

            $stmtVenta->execute([
                ':total' => $datosProvisorios['total'],
                ':forma_pago' => $datosExtraVenta['forma_pago'],
                ':tipo_pago' => $datosExtraVenta['tipo_pago'],
                ':descuento_pct' => $datosExtraVenta['descuento'],
                ':recargo_pct' => $datosExtraVenta['recargo'],
                ':id_cliente' => $cliente['id'],
                ':id_usuario' => $vendedor['id']
            ]);

            $idVenta = $stmtVenta->fetchColumn();

            if (!$idVenta) {
                $this->error = 'No se pudo crear la venta.';
                $this->db->rollBack();
                return false;
            }

            // 2. PREPARAR QUERIES AUXILIARES

            // Pedidos disponibles (FIFO)

            $sqlPedidos = "SELECT id_pedido, (cantidad - cantidad_vendida) AS disponible FROM pedido_detalle
                WHERE id_producto = :id_producto
                AND (cantidad - cantidad_vendida) > 0
                ORDER BY id_pedido ASC
            ";

            $stmtPedidos = $this->db->prepare($sqlPedidos);

            // Insert Detalle Venta
            
            $sqlDetalle = "INSERT INTO venta_detalle (precio_unitario, cantidad, subtotal, descuento_pct, id_venta, id_producto, id_pedido) VALUES
                (:precio_unitario, :cantidad, :subtotal, :descuento_pct, :id_venta, :id_producto, :id_pedido)
            ";

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

            // Update Pedido Detalle

            $sqlUpdatePedido = "UPDATE pedido_detalle
                SET cantidad_vendida = cantidad_vendida + :cantidad
                WHERE id_pedido = :id_pedido
                AND id_producto = :id_producto
            ";

            $stmtUpdatePedido = $this->db->prepare($sqlUpdatePedido);

            // Update stock del producto

            $sqlStock = "UPDATE productos
                SET stock = stock - :cantidad
                WHERE id = :id_producto
            ";

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

            // 3. PROCESAR DETALLE + CONSUMO STOCK (FIFO)

            foreach ($productosFinales as $p) {

                $cantidadRestante = $p['cantidad'];

                $stmtPedidos->execute([
                    ':id_producto' => $p['id_producto']
                ]);

                $pedidos = $stmtPedidos->fetchAll(PDO::FETCH_ASSOC);

                if (empty($pedidos)) {
                    $this->db->rollBack();
                    $this->error = "No hay stock disponible para el producto ID {$p['id_producto']}";
                    return false;
                }

                foreach ($pedidos as $pedido) {

                    if ($cantidadRestante <= 0) {
                        break;
                    }

                    $aVender = min($cantidadRestante, $pedido['disponible']);
                    $subtotal = $p['precio_final'] * $aVender;

                    // Insert venta_detalle

                    $stmtDetalle->execute([
                        ':precio_unitario' => $p['precio'],
                        ':cantidad' => $aVender,
                        ':subtotal' => $subtotal,
                        ':descuento_pct' => $p['descuento_total'],
                        ':id_venta' => $idVenta,
                        ':id_producto' => $p['id_producto'],
                        ':id_pedido' => $pedido['id_pedido'],
                    ]);

                    // Update pedido_detalle

                    $stmtUpdatePedido->execute([
                        ':cantidad' => $aVender,
                        ':id_pedido' => $pedido['id_pedido'],
                        ':id_producto' => $p['id_producto'],
                    ]);

                    // Update stock producto

                    $stmtStock->execute([
                        ':cantidad' => $aVender,
                        ':id_producto' => $p['id_producto'],
                    ]);

                    $cantidadRestante -= $aVender;
                }

                // VALIDACIÓN IMPORTANTE DE STOCK
                    
                if ($cantidadRestante > 0) {
                    $this->error = "Stock insuficiente para el producto ID {$p['id_producto']}";
                    return false;
                }
            }

            // 4. COMMIT

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

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

            // Log interno

            error_log($e->getMessage());

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

    // OBTENER PREVIEW DE CODIGO DE VENTA (no implementado)

    /*

    public function getNumeroVentaPreview(): int {

        $sql = "SELECT nextval('venta_preview_seq')";

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

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

    */

    // LISTAR VENTAS

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

        $sql = "SELECT v.id, v.fecha, v.total, v.forma_pago, v.tipo_pago, v.descuento_pct, v.recargo_pct, c.nombre AS cliente_nombre, c.apellido AS cliente_apellido, u.nombre AS vendedor_nombre, u.apellido AS vendedor_apellido FROM ventas AS v
                INNER JOIN clientes AS c ON (v.id_cliente = c.id)
                INNER JOIN usuarios AS u ON (v.id_usuario = u.id)
                WHERE EXTRACT(YEAR FROM v.fecha) = :anio
            ORDER BY v.id ASC";

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

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

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

    // OBTENER DETALLE DE VENTA

    public function getDetalleVenta(int $idVenta): ?array {

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

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

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

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