<?php
namespace App\Controllers;

use App\Models\ProductModel;
use App\Models\SaleModel;
use App\Models\CajaModel;
use App\Models\VentaPendienteModel;

class SaleController extends BaseController
{
    public function index()
    {
        $saleModel = new SaleModel();
        $showToday = $this->request->getGet('periodo') !== 'todas';
        $data['sales'] = $saleModel->getSalesWithDetails($showToday);
        $data['totalVentas'] = $saleModel->getTotalVentas();
        $data['totalGanancias'] = $saleModel->getTotalGanancias();
        $data['totalVentasHoy'] = $saleModel->getTotalSalesToday();
        $data['showToday'] = $showToday;
        $data['deletionHistory'] = $saleModel->getDeletionHistory(true);
        $data['deletionBackups'] = $saleModel->getDeletionBackups(true);
        $data['title'] = 'Lista de Ventas';
        return view('sales/index', $data);
    }

    public function create()
    {
        $productModel = new ProductModel();
        $data['products'] = $productModel->getProductsWithCategory();
        $data['categories'] = $productModel->getCategories();
        $data['paymentMethods'] = $productModel->getPaymentMethods();
        $data['title'] = 'Registrar Venta';
        return view('sales/create', $data);
    }

    public function store()
    {
        $validation = \Config\Services::validation();
        $validation->setRules([
            'id_producto'    => 'required',
            'id_producto.*'  => 'required|numeric',
            'cantidad'       => 'required',
            'cantidad.*'     => 'required|integer|greater_than[0]',
            'precio_unitario' => 'required',
            'precio_unitario.*' => 'required|numeric|greater_than[0]',
            'id_forma_pago'  => 'required|numeric'
        ]);

        if (!$validation->withRequest($this->request)->run()) {
            return redirect()->back()->withInput()->with('errors', $validation->getErrors());
        }

        $cajaModel = new \App\Models\CajaModel();
        $openCaja = $cajaModel->getOpenCaja();
        if (!$openCaja) {
            return redirect()->back()->with('error', 'No se puede registrar ventas: la caja no esta abierta.');
        }

        $productIds = $this->request->getPost('id_producto');
        $quantities = $this->request->getPost('cantidad');
        $unitPrices = $this->request->getPost('precio_unitario');

        $productModel = new ProductModel();
        $db = \Config\Database::connect();

        $productIds = $this->request->getPost('id_producto');
        $quantities = $this->request->getPost('cantidad');
        $unitPrices = $this->request->getPost('precio_unitario');

        $saleModel = new SaleModel();

        $totalGeneral = 0;
        $details = [];

        foreach ($productIds as $i => $productId) {
            $product = $productModel->find($productId);
            if (!$product) {
                return redirect()->back()->with('error', 'Producto #' . $productId . ' no encontrado');
            }

            $quantity = (int)$quantities[$i];
            if ($product['stock_actual'] < $quantity) {
                return redirect()->back()->withInput()->with('error', 'Stock insuficiente para ' . $product['nombre'] . ' (disponible: ' . $product['stock_actual'] . ')');
            }

            $unitPrice = (float)$unitPrices[$i];
            $subtotal = $quantity * $unitPrice;
            $totalGeneral += $subtotal;

            $details[] = [
                'id_producto'     => $product['id_producto'],
                'cantidad'        => $quantity,
                'precio_unitario' => $unitPrice,
                'costo_unitario'  => $product['precio_compra'],
                'subtotal'        => $subtotal,
                'product_name'    => $product['nombre']
            ];
        }

        $db->transStart();

        $saleModel->insert([
            'id_usuario'    => session()->get('user_id'),
            'total'         => $totalGeneral,
            'observaciones' => $this->request->getPost('observaciones')
        ]);
        $saleId = $saleModel->insertID();

        foreach ($details as $d) {
            $d['id_venta'] = $saleId;
            unset($d['product_name']);
            $db->table('venta_detalles')->insert($d);
        }

        $db->table('venta_pagos')->insert([
            'id_venta'      => $saleId,
            'id_forma_pago' => $this->request->getPost('id_forma_pago'),
            'monto'         => $totalGeneral
        ]);

        $db->transComplete();

        if ($db->transStatus()) {
            $cajaModel = new CajaModel();
            $openCaja = $cajaModel->getOpenCaja();
            if ($openCaja) {
                $cajaModel->addChicaMovement([
                    'id_caja'    => $openCaja['id_caja'],
                    'id_usuario' => session()->get('user_id'),
                    'tipo'       => 'ingreso',
                    'concepto'   => 'Venta #' . $saleId,
                    'monto'      => $totalGeneral
                ]);
            }
            foreach ($details as $d) {
                $updatedProduct = $productModel->find($d['id_producto']);
                if ($updatedProduct && $updatedProduct['stock_actual'] <= 5) {
                    session()->setFlashdata('warning', 'Stock minimo alcanzado para ' . $updatedProduct['nombre'] . '!');
                }
            }
            return redirect()->to('/sales')->with('success', 'Venta registrada correctamente.')->with('ticket_sale_id', $saleId);
        }
        return redirect()->back()->with('error', 'Error al registrar la venta');
    }

    public function show($id)
    {
        $saleModel = new SaleModel();
        $data['sale'] = $saleModel->getSaleWithDetails($id);
        if (!$data['sale']) {
            return redirect()->to('/sales')->with('error', 'Venta no encontrada');
        }
        $data['title'] = 'Detalle de Venta #' . $id;
        return view('sales/show', $data);
    }

    public function ticket($id)
    {
        $saleModel = new SaleModel();
        $data['sale'] = $saleModel->getSaleWithDetails($id);
        if (!$data['sale']) {
            return redirect()->to('/sales')->with('error', 'Venta no encontrada');
        }
        return view('sales/ticket', $data);
    }

    public function ticketContent($id)
    {
        $saleModel = new SaleModel();
        $data['sale'] = $saleModel->getSaleWithDetails($id);
        if (!$data['sale']) {
            return $this->response->setStatusCode(404)->setBody('Venta no encontrada');
        }
        return view('sales/ticket_content', $data);
    }

    public function edit($id)
    {
        $saleModel = new SaleModel();
        $data['sale'] = $saleModel->getSaleWithDetails($id);
        if (!$data['sale']) {
            return redirect()->to('/sales')->with('error', 'Venta no encontrada');
        }
        $productModel = new ProductModel();
        $data['paymentMethods'] = $productModel->getPaymentMethods();
        $data['title'] = 'Editar Venta #' . $id;
        return view('sales/edit', $data);
    }

    public function update($id)
    {
        $saleModel = new SaleModel();
        $sale = $saleModel->find($id);
        if (!$sale) {
            return redirect()->to('/sales')->with('error', 'Venta no encontrada');
        }

        $validation = \Config\Services::validation();
        $validation->setRules([
            'observaciones'  => 'permit_empty',
            'id_forma_pago'  => 'required|numeric',
            'detalle_cantidad.*' => 'required|integer|greater_than[0]'
        ]);

        if (!$validation->withRequest($this->request)->run()) {
            return redirect()->back()->withInput()->with('errors', $validation->getErrors());
        }

        $db = \Config\Database::connect();
        $db->transStart();

        $detalleIds = $this->request->getPost('detalle_id');
        $nuevasCantidades = $this->request->getPost('detalle_cantidad');
        $nuevosPrecios = $this->request->getPost('detalle_precio');
        $totalVenta = 0;

        foreach ($detalleIds as $i => $idDetalle) {
            $nuevaCant = (int)$nuevasCantidades[$i];
            $nuevoPrecio = (float)($nuevosPrecios[$i] ?? 0);

            $oldDetail = $db->table('venta_detalles')
                            ->where('id_detalle', $idDetalle)
                            ->get()
                            ->getRowArray();

            if (!$oldDetail) continue;

            $diferencia = $nuevaCant - $oldDetail['cantidad'];

            if ($diferencia > 0) {
                $product = $db->table('productos')
                              ->where('id_producto', $oldDetail['id_producto'])
                              ->get()
                              ->getRowArray();
                if ($product['stock_actual'] < $diferencia) {
                    $db->transRollback();
                    return redirect()->back()->withInput()->with('error', 'Stock insuficiente para ' . $product['nombre'] . '. Disponible: ' . $product['stock_actual']);
                }
                $db->table('productos')
                   ->set('stock_actual', 'stock_actual - ' . $diferencia, false)
                   ->where('id_producto', $oldDetail['id_producto'])
                   ->update();
            } elseif ($diferencia < 0) {
                $db->table('productos')
                   ->set('stock_actual', 'stock_actual + ' . abs($diferencia), false)
                   ->where('id_producto', $oldDetail['id_producto'])
                   ->update();
            }

            if ($nuevoPrecio <= 0) $nuevoPrecio = $oldDetail['precio_unitario'];
            $nuevoSubtotal = $nuevaCant * $nuevoPrecio;
            $totalVenta += $nuevoSubtotal;

            $db->table('venta_detalles')
               ->where('id_detalle', $idDetalle)
               ->update([
                   'cantidad'        => $nuevaCant,
                   'precio_unitario' => $nuevoPrecio,
                   'subtotal'        => $nuevoSubtotal
               ]);

            if ($diferencia != 0) {
                $db->table('stock_movimientos')->insert([
                    'id_producto'     => $oldDetail['id_producto'],
                    'tipo_movimiento' => 'ajuste',
                    'cantidad'        => abs($diferencia),
                    'id_usuario'      => session()->get('user_id'),
                    'observacion'     => 'Ajuste por edicion venta #' . $id . ' (' . ($diferencia > 0 ? 'aumento' : 'disminucion') . ')'
                ]);
            }
        }

        $saleModel->update($id, [
            'total'         => $totalVenta,
            'observaciones' => $this->request->getPost('observaciones')
        ]);

        $db->table('venta_pagos')
           ->where('id_venta', $id)
           ->update([
               'id_forma_pago' => $this->request->getPost('id_forma_pago'),
               'monto'         => $totalVenta
           ]);

        $db->transComplete();

        if ($db->transStatus()) {
            return redirect()->to('/sales')->with('success', 'Venta actualizada correctamente');
        }
        return redirect()->back()->with('error', 'Error al actualizar la venta');
    }

    public function delete($id)
    {
        $saleModel = new SaleModel();
        $sale = $saleModel->find($id);
        if (!$sale) {
            return redirect()->to('/sales')->with('error', 'Venta no encontrada');
        }

        if (date('Y-m-d', strtotime($sale['fecha'])) !== date('Y-m-d')) {
            return redirect()->back()->with('error', 'Solo se pueden eliminar ventas del dia de hoy');
        }

        $db = \Config\Database::connect();
        $db->transStart();

        $details = $db->table('venta_detalles')
                      ->where('id_venta', $id)
                      ->get()
                      ->getResultArray();

        $payments = $db->table('venta_pagos')
                       ->where('id_venta', $id)
                       ->get()
                       ->getResultArray();

        $backup = json_encode([
            'type' => 'delete_backup',
            'venta_id' => $id,
            'sale' => $sale,
            'details' => $details,
            'payments' => $payments
        ], JSON_UNESCAPED_UNICODE);

        foreach ($details as $detail) {
            $db->table('productos')
               ->set('stock_actual', 'stock_actual + ' . $detail['cantidad'], false)
               ->where('id_producto', $detail['id_producto'])
               ->update();

            $db->table('stock_movimientos')->insert([
                'id_producto'     => $detail['id_producto'],
                'tipo_movimiento' => $detail === reset($details) ? 'backup' : 'entrada',
                'cantidad'        => $detail['cantidad'],
                'id_usuario'      => session()->get('user_id'),
                'observacion'     => $detail === reset($details) ? $backup : 'Devolucion por eliminacion de venta #' . $id
            ]);
        }

        $saleModel->delete($id);
        $db->transComplete();

        if ($db->transStatus()) {
            $cajaModel = new CajaModel();
            $openCaja = $cajaModel->getOpenCaja();
            if ($openCaja) {
                $totalPagado = array_sum(array_column($payments, 'monto'));
                $cajaModel->addChicaMovement([
                    'id_caja'    => $openCaja['id_caja'],
                    'id_usuario' => session()->get('user_id'),
                    'tipo'       => 'gasto',
                    'concepto'   => 'Devolucion venta #' . $id,
                    'monto'      => $totalPagado
                ]);
            }
            return redirect()->to('/sales')->with('success', 'Venta eliminada correctamente. Stock restaurado.');
        }
        return redirect()->back()->with('error', 'Error al eliminar la venta');
    }

    public function restore($movimientoId)
    {
        $db = \Config\Database::connect();
        $mov = $db->table('stock_movimientos')
                  ->where('id_movimiento', $movimientoId)
                  ->get()
                  ->getRowArray();

        if (!$mov || strpos($mov['observacion'], '"type":"delete_backup"') === false) {
            return redirect()->to('/sales')->with('error', 'Respaldo no encontrado');
        }

        $data = json_decode($mov['observacion'], true);
        $db->transStart();

        $saleModel = new SaleModel();
        $saleModel->insert($data['sale']);
        $newId = $saleModel->insertID();

        foreach ($data['details'] as $d) {
            $d['id_venta'] = $newId;
            unset($d['id_detalle']);
            $db->table('venta_detalles')->insert($d);

            $db->table('productos')
               ->set('stock_actual', 'stock_actual - ' . $d['cantidad'], false)
               ->where('id_producto', $d['id_producto'])
               ->update();

            $db->table('stock_movimientos')->insert([
                'id_producto'     => $d['id_producto'],
                'tipo_movimiento' => 'salida',
                'cantidad'        => $d['cantidad'],
                'id_usuario'      => session()->get('user_id'),
                'observacion'     => 'Restauracion de venta eliminada #' . $data['venta_id'] . ' (nuevo #' . $newId . ')'
            ]);
        }

        foreach ($data['payments'] as $p) {
            $p['id_venta'] = $newId;
            unset($p['id_pago']);
            $db->table('venta_pagos')->insert($p);
        }

        $db->table('stock_movimientos')
           ->where('id_movimiento', $movimientoId)
           ->delete();

        $db->transComplete();

        if ($db->transStatus()) {
            return redirect()->to('/sales')->with('success', 'Venta #' . $data['venta_id'] . ' restaurada correctamente como #' . $newId);
        }
        return redirect()->back()->with('error', 'Error al restaurar la venta');
    }

    public function savePending()
    {
        if ($this->request->getMethod() !== 'POST') {
            return $this->response->setStatusCode(405)->setJSON(['error' => 'Method not allowed']);
        }

        $cart = $this->request->getPost('cart');
        if (!$cart) {
            return $this->response->setStatusCode(400)->setJSON(['error' => 'Cart data required']);
        }

        $userId = session()->get('user_id');
        $model = new VentaPendienteModel();

        $existing = $model->where('id_usuario', $userId)->first();
        if ($existing) {
            $model->update($existing['id_pendiente'], ['datos' => $cart]);
            $id = $existing['id_pendiente'];
        } else {
            $id = $model->insert(['id_usuario' => $userId, 'datos' => $cart]);
        }

        return $this->response->setJSON(['success' => true, 'id' => $id]);
    }

    public function getPending()
    {
        $userId = session()->get('user_id');
        $model = new VentaPendienteModel();
        $pending = $model->where('id_usuario', $userId)->first();

        if ($pending) {
            return $this->response->setJSON(['success' => true, 'datos' => json_decode($pending['datos'], true)]);
        }
        return $this->response->setJSON(['success' => false, 'message' => 'No hay ventas pendientes']);
    }

    public function clearPending()
    {
        $userId = session()->get('user_id');
        $model = new VentaPendienteModel();
        $model->where('id_usuario', $userId)->delete();

        if ($this->request->hasHeader('HX-Request')) {
            return $this->response->setHeader('HX-Trigger', json_encode([
                'pendingCleared' => true
            ]));
        }

        return $this->response->setJSON(['success' => true]);
    }

    public function getPendingHtml()
    {
        $userId = session()->get('user_id');
        $model = new VentaPendienteModel();
        $pending = $model->where('id_usuario', $userId)->first();

        if (!$pending) {
            return $this->response->setBody('');
        }

        $data = json_decode($pending['datos'], true);
        $count = is_array($data) ? count($data) : 0;

        if ($count === 0) {
            return $this->response->setBody('');
        }

        $html = '<div id="pendingBanner" class="flex items-center gap-2 px-4 py-2 text-xs text-amber-800 bg-amber-50 border-b border-amber-200">'
              . '    <i class="bi bi-clock-history text-amber-500"></i>'
              . '    <span>Tienes una venta pendiente con <strong>' . $count . '</strong> producto(s)</span>'
              . '    <button type="button" onclick="restorePendingCart()" class="bg-amber-500 hover:bg-amber-600 text-white text-xs font-medium px-2.5 py-1 rounded border-0 cursor-pointer ml-auto whitespace-nowrap">Restaurar</button>'
              . '    <button type="button" hx-post="' . site_url('sales/clear-pending') . '" hx-trigger="click" hx-target="#pendingBanner" hx-swap="outerHTML" class="text-amber-600 hover:text-amber-800 p-1 border-0 bg-transparent cursor-pointer"><i class="bi bi-x"></i></button>'
              . '</div>';

        return $this->response->setBody($html);
    }
}
