<?php

namespace App\Controllers;

use App\Models\ProductoModel;
use App\Models\ReservaModel;
use App\Models\PagoModel;
use App\Models\ClienteMembresiaModel;
use App\Models\UsuarioModel;
use App\Models\PromocionModel;
use App\Models\PromocionProductoModel;
use App\Entities\Reserva as ReservaEntity;
use App\Entities\Pago as PagoEntity;
use App\Libraries\BoletoGenerator;
use App\Libraries\PromoHelper;

class Alimentos extends BaseController
{
    private const CART_KEY = 'carrito_temp';

    public function agregarCarrito()
    {
        $idProducto = (int)$this->request->getPost('id_producto');
        $cantidad = max(1, (int)$this->request->getPost('cantidad'));

        $productoModel = new ProductoModel();
        $producto = $productoModel->find($idProducto);

        if (!$producto || !$producto->estaDisponible()) {
            return $this->response->setJSON([ 'success' => false, 'message' => 'Producto no encontrado o no disponible.' ]);
        }

        $carrito = $this->getCarrito();
        $lineKey = 'product_' . $producto->id_producto;

        if (isset($carrito['items'][$lineKey])) {
            $carrito['items'][$lineKey]['cantidad'] += $cantidad;
        } else {
            $carrito['items'][$lineKey] = [
                'line_key' => $lineKey,
                'id_producto' => $producto->id_producto,
                'nombre' => $producto->nombre,
                'descripcion' => $producto->descripcion,
                'categoria' => $producto->categoria,
                'imagen_url' => $producto->getImagenUrl(),
                'precio_unitario' => (float)$producto->precio,
                'cantidad' => $cantidad,
                'id_promocion' => null,
            ];
        }

        $carrito = $this->normalizeCarrito($carrito['items']);
        session()->set(self::CART_KEY, $carrito);

        return $this->response->setJSON([ 'success' => true, 'message' => 'Producto agregado al carrito.', 'cart' => $carrito ]);
    }

    /**
     * Agrega al carrito los productos de una promoción de tipo COMBO/PRODUCTO/GENERAL.
     * Responde en JSON cuando la llamada es AJAX (botón "Agregar al carrito" en /promociones),
     * y con un redirect + flash cuando es una navegación normal (fallback sin JS).
     */
    public function agregarPromocion($id_promocion)
    {
        $esAjax = $this->request->isAJAX() || strtolower((string) $this->request->getPost('ajax')) === '1';

        $promocionModel = new PromocionModel();
        $promocion = $promocionModel->find($id_promocion);

        if (!$promocion) {
            return $this->fallarPromocion($esAjax, 'Promoción no disponible o inactiva.');
        }

        if ($promocion->esTipoEntrada()) {
            return $this->fallarPromocion($esAjax, 'Esta promoción es para entradas: úsala desde el botón "Aprovechar promoción".');
        }

        $dias = PromoHelper::diasValidos($promocion->id_promocion);
        $vigencia = PromoHelper::verificarVigencia($promocion, $dias);
        if (!$vigencia['vigente']) {
            return $this->fallarPromocion($esAjax, $vigencia['motivo']);
        }

        $promoProductoModel = new PromocionProductoModel();
        $productosPromo = $promoProductoModel->getProductosDePromocion($id_promocion);

        // Sólo se agregan los productos aún disponibles; si ninguno lo está, se aborta.
        $productosDisponibles = array_values(array_filter($productosPromo, function ($p) {
            return (int) ($p['disponible'] ?? 0) === 1;
        }));

        if (empty($productosDisponibles)) {
            return $this->fallarPromocion($esAjax, 'Esta promoción no está asociada a ningún producto disponible.');
        }

        $bundle = PromoHelper::calcularBundle($promocion, $productosDisponibles);
        $carrito = $this->getCarrito();

        foreach ($bundle['items'] as $item) {
            $lineKey = 'promo_' . $promocion->id_promocion . '_' . $item['id_producto'];
            $cantidad = max(1, (int) $item['cantidad']);

            $productoModel = new ProductoModel();
            $productoEntity = $productoModel->find($item['id_producto']);
            $imagenUrl = $productoEntity ? $productoEntity->getImagenUrl() : base_url('img/placeholder-poster.jpg');

            if (isset($carrito['items'][$lineKey])) {
                $carrito['items'][$lineKey]['cantidad'] += $cantidad;
            } else {
                $carrito['items'][$lineKey] = [
                    'line_key' => $lineKey,
                    'id_producto' => $item['id_producto'],
                    'nombre' => $item['nombre'],
                    'descripcion' => $item['descripcion'],
                    'categoria' => $item['categoria'],
                    'imagen_url' => $imagenUrl,
                    'precio_unitario' => $item['precio_unitario_promo'],
                    'cantidad' => $cantidad,
                    'id_promocion' => $promocion->id_promocion,
                ];
            }
        }

        $carrito = $this->normalizeCarrito($carrito['items']);
        session()->set(self::CART_KEY, $carrito);

        $mensaje = '"' . $promocion->nombre . '" se agregó a tu carrito.';

        if ($esAjax) {
            return $this->response->setJSON([
                'success' => true,
                'message' => $mensaje,
                'cart' => $carrito,
            ]);
        }

        return redirect()->to(base_url('alimentos'))->with('success', $mensaje);
    }

    /**
     * Respuesta uniforme de error para agregarPromocion(), en JSON o vía flash/redirect
     * según el tipo de petición.
     */
    private function fallarPromocion(bool $esAjax, string $mensaje)
    {
        if ($esAjax) {
            return $this->response->setJSON(['success' => false, 'message' => $mensaje]);
        }

        return redirect()->to(base_url('promociones'))->with('error', $mensaje);
    }

    public function eliminarCarrito()
    {
        $lineKey = $this->request->getPost('line_key') ?? $this->request->getPost('id_producto');
        $carrito = $this->getCarrito();

        if ($lineKey && isset($carrito['items'][$lineKey])) {
            unset($carrito['items'][$lineKey]);
        } elseif (is_numeric($lineKey)) {
            foreach ($carrito['items'] as $key => $item) {
                if ((int)$item['id_producto'] === (int)$lineKey) {
                    unset($carrito['items'][$key]);
                }
            }
        }

        $carrito = $this->normalizeCarrito($carrito['items']);
        session()->set(self::CART_KEY, $carrito);

        return $this->response->setJSON([ 'success' => true, 'message' => 'Producto eliminado del carrito.', 'cart' => $carrito ]);
    }

    public function actualizarCarrito()
    {
        $lineKey = $this->request->getPost('line_key');
        $cantidad = (int)$this->request->getPost('cantidad');
        $carrito = $this->getCarrito();

        if ($lineKey && isset($carrito['items'][$lineKey])) {
            if ($cantidad <= 0) {
                unset($carrito['items'][$lineKey]);
            } else {
                $carrito['items'][$lineKey]['cantidad'] = $cantidad;
            }
        } elseif (is_numeric($lineKey)) {
            $idProducto = (int)$lineKey;
            if ($cantidad <= 0) {
                foreach ($carrito['items'] as $key => $item) {
                    if ((int)$item['id_producto'] === $idProducto) {
                        unset($carrito['items'][$key]);
                    }
                }
            } else {
                $productoModel = new ProductoModel();
                $producto = $productoModel->find($idProducto);

                if (!$producto || !$producto->estaDisponible()) {
                    return $this->response->setJSON([ 'success' => false, 'message' => 'Producto no encontrado o no disponible.' ]);
                }

                $foundKey = null;
                foreach ($carrito['items'] as $key => $item) {
                    if ((int)$item['id_producto'] === $idProducto) {
                        $foundKey = $key;
                        break;
                    }
                }

                if ($foundKey !== null) {
                    $carrito['items'][$foundKey]['cantidad'] = $cantidad;
                } else {
                    $newKey = 'product_' . $producto->id_producto;
                    $carrito['items'][$newKey] = [
                        'line_key' => $newKey,
                        'id_producto' => $producto->id_producto,
                        'nombre' => $producto->nombre,
                        'descripcion' => $producto->descripcion,
                        'categoria' => $producto->categoria,
                        'imagen_url' => $producto->getImagenUrl(),
                        'precio_unitario' => (float)$producto->precio,
                        'cantidad' => $cantidad,
                    ];
                }
            }
        }

        $carrito = $this->normalizeCarrito($carrito['items']);
        session()->set(self::CART_KEY, $carrito);

        return $this->response->setJSON([ 'success' => true, 'message' => 'Carrito actualizado correctamente.', 'cart' => $carrito ]);
    }

    public function pago()
    {
        if (!session()->get('isLoggedIn')) {
            return redirect()->to(base_url('login'))->with('error', 'Por favor, inicia sesión para completar tu compra de alimentos.');
        }

        $carrito = $this->getCarrito();
        if (empty($carrito['items'])) {
            return redirect()->to(base_url('alimentos'))->with('error', 'Tu carrito está vacío. Agrega productos antes de pagar.');
        }

        return view('home/alimentos/pago', [
            'carrito' => $carrito
        ]);
    }

    public function confirmar()
    {
        if (!session()->get('isLoggedIn')) {
            return redirect()->to(base_url('login'))->with('error', 'Por favor, inicia sesión para completar tu compra de alimentos.');
        }

        $carrito = $this->getCarrito();
        if (empty($carrito['items'])) {
            return redirect()->to(base_url('alimentos'))->with('error', 'Tu carrito está vacío. Agrega productos antes de pagar.');
        }

        $rules = [
            'numero_tarjeta' => 'required',
            'nombre_titular' => 'required|min_length[3]|max_length[100]',
            'fecha_vencimiento' => 'required',
            'cvv' => 'required|numeric|min_length[3]|max_length[4]'
        ];

        if (!$this->validate($rules)) {
            return redirect()->back()->withInput()->with('error', 'Por favor, verifica los datos de pago antes de continuar.');
        }

        $usuarioModel = new UsuarioModel();
        $usuario = $usuarioModel->find(session()->get('user_id'));
        if (!$usuario) {
            session()->destroy();
            return redirect()->to(base_url('login'))->with('error', 'Tu sesión ya no es válida. Inicia sesión nuevamente para continuar.');
        }

        $carritoGuardado = $carrito;
        session()->remove(self::CART_KEY);

        $db = db_connect();
        $db->transException(true);

        try {
            $db->transStart();

            $reservaModel = new ReservaModel();
            $reserva = new ReservaEntity();
            $reserva->id_usuario = session()->get('user_id');
            $reserva->total = $carritoGuardado['total'];
            $reserva->estado = 'pagada';

            if (!$reservaModel->save($reserva)) {
                throw new \RuntimeException('No se pudo guardar la reserva: ' . implode(' ', $reservaModel->errors()));
            }

            $idReserva = $reservaModel->getInsertID();

            foreach ($carritoGuardado['items'] as $item) {
                $db->table('reserva_producto')->insert([
                    'id_reserva' => $idReserva,
                    'id_producto' => $item['id_producto'],
                    'id_promocion' => $item['id_promocion'] ?? null,
                    'cantidad' => $item['cantidad'],
                    'precio_unitario' => $item['precio_final']
                ]);
            }

            $pagoModel = new PagoModel();
            $pago = new PagoEntity();
            $pago->id_reserva = $idReserva;
            $pago->monto = $carritoGuardado['total'];
            $pago->metodo = 'tarjeta';
            $pago->estado = 'aprobado';
            $referencia = 'ALM-' . date('Y') . '-' . sprintf('%06d', $idReserva);
            $pago->referencia = $referencia;

            if (!$pagoModel->save($pago)) {
                throw new \RuntimeException('No se pudo guardar el pago: ' . implode(' ', $pagoModel->errors()));
            }

            $db->transComplete();

            if ($db->transStatus() === false) {
                throw new \RuntimeException('La transacción no pudo completarse.');
            }
        } catch (\Throwable $e) {
            $db->transRollback();
            session()->set(self::CART_KEY, $carritoGuardado);
            log_message('error', 'Error al confirmar compra de alimentos: ' . $e->getMessage());
            return redirect()->to(base_url('alimentos/pago'))->with('error', 'Ocurrió un problema al procesar tu pago. Intenta nuevamente.');
        }

        $correoEnviado = false;
        try {
            $generator = new BoletoGenerator();
            $pdfPath = $generator->generarBoleto($idReserva);
            if ($usuario && !empty($usuario->email)) {
                $correoEnviado = $generator->enviarPorCorreo(
                    $usuario->email,
                    $usuario->nombres,
                    $referencia,
                    $pdfPath,
                    'alimentos'
                );
            }
        } catch (\Throwable $e) {
            log_message('error', 'Error generando boleta o enviando correo de alimentos: ' . $e->getMessage());
        }

        if ($correoEnviado) {
            session()->setFlashdata('success_email', 'Se ha enviado la boleta de compra de alimentos a tu correo electrónico.');
        } else {
            session()->setFlashdata('warning_email', 'La boleta de alimentos se generó correctamente. No se pudo enviar por correo (verifica SMTP).');
        }

        return redirect()->to(base_url('alimentos/confirmacion/' . $idReserva));
    }

    public function confirmacion($idReserva)
    {
        if (!session()->get('isLoggedIn')) {
            return redirect()->to(base_url('login'))->with('error', 'Por favor, inicia sesión para ver tu boleta de alimentos.');
        }

        $db = db_connect();
        $reserva = $db->query("SELECT r.*, p.referencia, p.metodo, p.fecha_pago
            FROM reserva r
            JOIN pago p ON p.id_reserva = r.id_reserva
            WHERE r.id_reserva = ? AND r.id_usuario = ?", [$idReserva, session()->get('user_id')])->getRow();

        if (!$reserva) {
            return redirect()->to(base_url('alimentos'))->with('error', 'No se encontró la compra de alimentos.');
        }

        $productos = $db->query("SELECT rp.*, pr.nombre, pr.descripcion, pr.categoria, pr.imagen_url
            FROM reserva_producto rp
            JOIN producto pr ON pr.id_producto = rp.id_producto
            WHERE rp.id_reserva = ?", [$idReserva])->getResult();

        return view('home/alimentos/confirmacion', [
            'reserva' => $reserva,
            'productos' => $productos
        ]);
    }

    public function descargarBoleta($idReserva)
    {
        if (!session()->get('isLoggedIn')) {
            return redirect()->to(base_url('login'))->with('error', 'Por favor, inicia sesión para descargar tu boleta.');
        }

        $reservaModel = new ReservaModel();
        $reserva = $reservaModel->where('id_reserva', $idReserva)
            ->where('id_usuario', session()->get('user_id'))
            ->first();

        if (!$reserva) {
            return redirect()->back()->with('error', 'Acceso no autorizado a esta boleta.');
        }

        $generator = new BoletoGenerator();
        $pdfPath = $generator->generarBoleto($idReserva);

        if (file_exists($pdfPath)) {
            return $this->response->download($pdfPath, null)->setFileName('Boleta-Alimentos-' . $reserva->id_reserva . '.pdf');
        }

        return redirect()->back()->with('error', 'El archivo de la boleta no está disponible en este momento.');
    }

    private function getCarrito(): array
    {
        $carrito = session()->get(self::CART_KEY);
        if (!is_array($carrito)) {
            return [
                'items' => [],
                'subtotal' => 0.0,
                'descuento_pct' => 0.0,
                'descuento_monto' => 0.0,
                'total' => 0.0
            ];
        }
        return $carrito;
    }

    private function normalizeCarrito(array $items): array
    {
        $descuentoPct = 0.0;
        if (session()->get('isLoggedIn')) {
            $cmModel = new ClienteMembresiaModel();
            $membresiaActiva = $cmModel->getMembresiaActiva(session()->get('user_id'));
            $descuentoPct = $membresiaActiva ? (float)$membresiaActiva->descuento_pct : 0.0;
        }

        $subtotalOriginal = 0.0;
        $subtotalFinal = 0.0;

        foreach ($items as $key => &$item) {
            $item['cantidad'] = max(1, (int)$item['cantidad']);
            $item['precio_unitario'] = (float)$item['precio_unitario'];
            $usePromocion = !empty($item['id_promocion']);
            $item['precio_final'] = $usePromocion
                ? $item['precio_unitario']
                : round($item['precio_unitario'] * (1 - $descuentoPct / 100), 2);
            $item['subtotal'] = round($item['precio_final'] * $item['cantidad'], 2);
            $item['line_key'] = $key;
            $subtotalOriginal += round($item['precio_unitario'] * $item['cantidad'], 2);
            $subtotalFinal += $item['subtotal'];
        }
        unset($item);

        $descuentoMonto = round($subtotalOriginal - $subtotalFinal, 2);
        $total = round($subtotalFinal, 2);

        return [
            'items' => $items,
            'subtotal' => $subtotalOriginal,
            'descuento_pct' => $descuentoPct,
            'descuento_monto' => $descuentoMonto,
            'total' => $total
        ];
    }
}
