<?php

namespace App\Services;
use App\Services\ProductionService;

use Config\Services;

class LogisticaService
{


    public static function findLineaEnvioPorEnvio(int $envio_id)
    {
        $db = \Config\Database::connect();

        // 1. Dirección del envío actual
        $envio = $db->table('envios')->select('direccion')->where('id', $envio_id)->get()->getRow();
        if (!$envio) {
            return $db->table('(SELECT NULL AS id, NULL AS name) AS empty')->where('1 = 0');
        }

        $direccionNormalizada = str_replace(' ', '', strtolower(trim($envio->direccion)));
        $direccionSQL = $db->escape($direccionNormalizada);

        // 2. Obtener presupuestos con esa dirección
        $presupuestosConEsaDireccion = $db->table('presupuesto_direcciones')
            ->select('presupuesto_id')
            ->where("REPLACE(LOWER(TRIM(direccion)), ' ', '') = $direccionSQL", null, false)
            ->get()
            ->getResultArray();

        $presupuestoIds = array_column($presupuestosConEsaDireccion, 'presupuesto_id');
        if (empty($presupuestoIds)) {
            return $db->table('(SELECT NULL AS id, NULL AS name) AS empty')->where('1 = 0');
        }

        // 3. Subconsulta principal
        $subBuilder = $db->table('pedidos_linea pl')
            ->select("
            ot.id AS id,
            CONCAT('[', ot.id, '] - ', pr.titulo) AS name,
            (
                SELECT IFNULL(SUM(el.unidades_envio), 0)
                FROM envios_lineas el
                JOIN envios e ON e.id = el.envio_id
                WHERE el.pedido_id = p.id
                AND el.presupuesto_id = pr.id
                AND REPLACE(LOWER(TRIM(e.direccion)), ' ', '') = $direccionSQL
                AND (e.finalizado = 1 OR e.id = $envio_id)
            ) AS unidades_enviadas,
            pd.cantidad AS cantidad
        ")
            ->join('pedidos p', 'p.id = pl.pedido_id')
            ->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
            ->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id')
            ->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
            ->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
            ->whereIn('pr.id', $presupuestoIds)
            ->whereIn('p.estado', ['finalizado', 'produccion'])
            ->where('ot_dates.embalaje_at IS NOT NULL')
            ->where("NOT EXISTS (
            SELECT 1
            FROM envios_lineas el
            WHERE el.envio_id = $envio_id
            AND el.pedido_id = p.id
            AND el.presupuesto_id = pr.id
            GROUP BY el.pedido_id, el.presupuesto_id
            HAVING SUM(el.unidades_envio) >= pd.cantidad
        )", null, false)
            ->groupBy('pl.id');

        // 4. Envolver y filtrar por unidades pendientes
        $builder = $db->table("({$subBuilder->getCompiledSelect(false)}) AS sub");
        $builder->select('id, name');
        $builder->where('cantidad > unidades_enviadas');
        $builder->orderBy('name', 'ASC');

        return $builder;
    }


    public static function findForNewEnvio()
    {
        $db = \Config\Database::connect();

        // 3. Subconsulta principal
        $subBuilder = $db->table('pedidos_linea pl')
            ->select("
            ot.id AS id,
            CONCAT('[', ot.id, '] - ', pr.titulo) AS name,
            (
                SELECT IFNULL(SUM(el.unidades_envio), 0)
                FROM envios_lineas el
                JOIN envios e ON e.id = el.envio_id
                WHERE el.pedido_id = p.id
                AND el.presupuesto_id = pr.id
                AND e.finalizado = 1 
                AND e.tipo_envio = 'estandar'
            ) AS unidades_enviadas,
            pd.cantidad AS cantidad
        ")
            ->join('pedidos p', 'p.id = pl.pedido_id')
            ->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
            ->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id')
            ->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
            ->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
            ->whereIn('p.estado', ['finalizado', 'produccion'])
            ->where('ot_dates.embalaje_at IS NOT NULL')
            ->groupBy('pl.id');

        // 4. Envolver y filtrar por unidades pendientes
        $builder = $db->table("({$subBuilder->getCompiledSelect(false)}) AS sub");
        $builder->select('id, name');
        $builder->where('cantidad > unidades_enviadas');
        $builder->orderBy('name', 'ASC');

        return $builder;
    }

    public static function findForNewEnvioFerro()
    {
        $db = \Config\Database::connect();

        // 3. Subconsulta principal
        $subBuilder = $db->table('pedidos_linea pl')
            ->select("
            ot.id AS id,
            CONCAT('[', ot.id, '] - ', pr.titulo) AS name,
            p.id as pedido_id,
            pr.id as presupuesto_id
        ")
            ->join('pedidos p', 'p.id = pl.pedido_id')
            ->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
            ->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id')
            ->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
            ->join('orden_trabajo_dates ot_dates', 'ot_dates.orden_trabajo_id = ot.id')
            ->whereIn('p.estado', ['finalizado', 'produccion'])
            ->where('ot_dates.pendiente_ferro_at IS NOT NULL')
            ->where('pd.is_ferro_prototipo', 1)
            ->groupBy('pl.id');

        // 4. Envolver y filtrar por unidades pendientes
        $builder = $db->table("({$subBuilder->getCompiledSelect(false)}) AS sub");
        $builder->select('id, name');
        $builder->orderBy('name', 'ASC');

        $builder->where("
        NOT EXISTS (
            SELECT 1
            FROM envios e
            INNER JOIN envios_lineas le ON le.envio_id = e.id
            WHERE e.tipo_envio = 'ferro_prototipo'
              AND (le.pedido_id = sub.pedido_id OR le.presupuesto_id = sub.presupuesto_id)
        )
            ", null, false);

        return $builder;
    }

    public static function findDireccionesNewEnvio($ot_id, $searchVal = "")
    {

        $direcciones = [];
        $counter = 1;

        $PresupuestoDireccionesModel = model('App\Models\Presupuestos\PresupuestoDireccionesModel');
        $dirs = $PresupuestoDireccionesModel->select('direccion')
            ->join('presupuestos pr', 'pr.id=presupuesto_direcciones.presupuesto_id')
            ->join('pedidos_linea pl', 'pl.presupuesto_id = pr.id')
            ->join('pedidos p', 'pl.pedido_id=p.id')
            ->join('ordenes_trabajo ot', 'ot.pedido_id = p.id')
            ->where('ot.id', $ot_id)
            ->where("presupuesto_direcciones.is_ferro_prototipo", 0);
        if ($searchVal != "") {
            $dirs = $dirs->groupStart()
                ->Like("id", $searchVal)
                ->groupEnd();
        }
        $dirs = $dirs->orderBy('direccion', 'asc')->findAll();
        foreach ($dirs as $key => $direccion) {
            $modelEnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
            $unidades_en_direccion = $modelEnvioLineasModel->select('SUM(envios_lineas.unidades_envio) as unidades_enviadas, 
                    envios_lineas.unidades_total')
                ->join('envios', 'envios.id = envios_lineas.envio_id')
                ->join('pedidos_linea', 'pedidos_linea.pedido_id = envios_lineas.pedido_id')
                ->join('pedidos', 'pedidos.id = pedidos_linea.pedido_id')
                ->join('ordenes_trabajo', 'ordenes_trabajo.pedido_id = pedidos.id')
                ->where('ordenes_trabajo.id', $ot_id)
                ->where('envios.direccion', $direccion->direccion)
                ->where('envios.finalizado', 1)
                ->groupBy('ordenes_trabajo.pedido_id')->get()->getResult();
            if (count($unidades_en_direccion) == 0 || $unidades_en_direccion[0]->unidades_enviadas < $unidades_en_direccion[0]->unidades_total) {
                array_push(
                    $direcciones,
                    (object) [
                        "id" => $counter,
                        'name' => $direccion->direccion
                    ]
                );
                $counter++;
            }
        }
        return $direcciones;

    }

    public static function addLineaEnvio($envio_id = null, $pedido_id = null, $direccion = null)
    {
        $db = \Config\Database::connect();

        $direccionNormalizada = strtolower(trim($direccion));

        // 1. Obtener presupuesto_id y cantidad desde presupuesto_direcciones
        $builder = $db->table('pedidos_linea pl')
            ->select("
                p.id AS pedido_id,
                pl.id AS linea_id,
                pr.id AS presupuesto_id,
                pd.cantidad AS total_unidades,
    
                (
                    SELECT IFNULL(SUM(el.unidades_envio), 0)
                    FROM envios_lineas el
                    JOIN envios e ON e.id = el.envio_id
                    WHERE el.pedido_id = p.id
                    AND el.presupuesto_id = pr.id
                    AND TRIM(LOWER(e.direccion)) = '$direccionNormalizada'
                    AND (e.finalizado = 1 OR e.id = $envio_id)
                ) AS unidades_enviadas,
    
                (
                    pd.cantidad - (
                        SELECT IFNULL(SUM(el2.unidades_envio), 0)
                        FROM envios_lineas el2
                        JOIN envios e2 ON e2.id = el2.envio_id
                        WHERE el2.pedido_id = p.id
                        AND el2.presupuesto_id = pr.id
                        AND TRIM(LOWER(e2.direccion)) = '$direccionNormalizada'
                        AND (e2.finalizado = 1 OR e2.id = $envio_id)
                    )
                ) AS unidades_envio
            ")
            ->join('pedidos p', 'p.id = pl.pedido_id')
            ->join('presupuestos pr', 'pr.id = pl.presupuesto_id')
            ->join('presupuesto_direcciones pd', 'pd.presupuesto_id = pr.id')
            ->where('pd.is_ferro_prototipo', 0)
            ->where('p.id', $pedido_id)
            ->whereIn('p.estado', ['finalizado', 'produccion'])
            ->where("TRIM(LOWER(pd.direccion)) = '$direccionNormalizada'", null, false)
            ->groupBy('pl.id');

        $result = $builder->get()->getResultObject();

        if (empty($result)) {
            return [
                'status' => false,
                'message' => lang('Logistica.errors.notFound'),
            ];
        }

        $r = $result[0];

        // 2. Insertar solo si hay unidades a enviar
        if ($r->unidades_envio > 0) {
            $EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
            $EnvioLineasModel->save([
                'envio_id' => $envio_id,
                'pedido_id' => $r->pedido_id,
                'presupuesto_id' => $r->presupuesto_id,
                'unidades_envio' => $r->unidades_envio,
                'unidades_total' => $r->total_unidades,
                'cajas' => null,
                'unidades_cajas' => 1,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
                'created_by' => auth()->user()->id,
                'updated_by' => auth()->user()->id,
            ]);
        }

        return [
            'status' => true,
            'data' => [
                'unidades_envio' => $r->unidades_envio,
                'unidades_enviadas' => $r->unidades_enviadas,
                'total_unidades' => $r->total_unidades,
            ]
        ];
    }



    public static function generateEnvio($ot_id, $direccion = null)
    {
        $presupuestoDireccionesModel = model('App\Models\Presupuestos\PresupuestoDireccionesModel');
        $direccionNormalizada = strtolower(trim($direccion));

        $datosEnvio = $presupuestoDireccionesModel
            ->select("
            presupuestos.id as presupuesto_id,
            presupuesto_direcciones.att,
            presupuesto_direcciones.direccion,
            presupuesto_direcciones.provincia as ciudad,
            presupuesto_direcciones.cp,
            presupuesto_direcciones.telefono,
            presupuesto_direcciones.email,
            presupuesto_direcciones.pais_id,
            presupuesto_direcciones.cantidad as cantidad_total,
            presupuestos.cliente_id as cliente_id,
            ordenes_trabajo.pedido_id as pedido_id,

            (
                presupuesto_direcciones.cantidad - IFNULL((
                    SELECT SUM(el.unidades_envio)
                    FROM envios_lineas el
                    JOIN envios e ON e.id = el.envio_id
                    WHERE el.pedido_id = pedidos.id
                    AND el.presupuesto_id = presupuestos.id
                    AND e.direccion LIKE " . $presupuestoDireccionesModel->db->escape("%$direccion%") . "
                    AND e.finalizado = 1
                ), 0)
            ) as cantidad
        ")
            ->join('pedidos_linea', 'pedidos_linea.presupuesto_id = presupuesto_direcciones.presupuesto_id')
            ->join('pedidos', 'pedidos.id = pedidos_linea.pedido_id')
            ->join('presupuestos', 'pedidos_linea.presupuesto_id = presupuestos.id')
            ->join('ordenes_trabajo', 'ordenes_trabajo.pedido_id = pedidos.id')
            ->where('ordenes_trabajo.id', $ot_id)
            ->where('presupuesto_direcciones.is_ferro_prototipo', 0)
            ->like('presupuesto_direcciones.direccion', $direccion)
            ->groupBy('presupuesto_direcciones.id')
            ->first();

        // Validación si no hay datos o no quedan unidades
        if (empty($datosEnvio) || $datosEnvio->cantidad <= 0) {
            return [
                'status' => false,
                'message' => lang('Logistica.errors.noAddresses') . ' o no hay unidades pendientes.',
            ];
        }

        // Crear envío
        $EnvioModel = model('App\Models\Logistica\EnvioModel');
        $EnvioModel->set([
            'cliente_id' => $datosEnvio->cliente_id,
            'att' => $datosEnvio->att,
            'direccion' => $datosEnvio->direccion,
            'ciudad' => $datosEnvio->ciudad,
            'cp' => $datosEnvio->cp,
            'telefono' => $datosEnvio->telefono,
            'email' => $datosEnvio->email,
            'pais_id' => $datosEnvio->pais_id,
            'cantidad' => $datosEnvio->cantidad,
            'cajas' => 1,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
        ]);
        $EnvioModel->insert();
        $idEnvio = $EnvioModel->insertID();

        // Crear línea de envío
        $EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
        $EnvioLineasModel->save([
            'envio_id' => $idEnvio,
            'pedido_id' => $datosEnvio->pedido_id,
            'unidades_envio' => $datosEnvio->cantidad,
            'unidades_total' => $datosEnvio->cantidad_total,
            'cajas' => 1,
            'unidades_cajas' => 1,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
            'created_by' => auth()->user()->id,
            'updated_by' => auth()->user()->id,
            'presupuesto_id' => (int) $datosEnvio->presupuesto_id
        ]);

        return [
            'status' => true,
            'data' => [
                'id_envio' => $idEnvio,
            ],
        ];
    }


    public static function generateEnvioFerro($ot_id)
    {
        $presupuestoDireccionesModel = model('App\Models\Presupuestos\PresupuestoDireccionesModel');

        $datosEnvio = $presupuestoDireccionesModel
            ->select("
            presupuestos.id as presupuesto_id,
            presupuesto_direcciones.att,
            presupuesto_direcciones.direccion,
            presupuesto_direcciones.provincia as ciudad,
            presupuesto_direcciones.cp,
            presupuesto_direcciones.telefono,
            presupuesto_direcciones.email,
            presupuesto_direcciones.pais_id,
            presupuesto_direcciones.cantidad as cantidad_total,
            presupuestos.cliente_id as cliente_id,
            ordenes_trabajo.pedido_id as pedido_id,
        ")
            ->join('pedidos_linea', 'pedidos_linea.presupuesto_id = presupuesto_direcciones.presupuesto_id')
            ->join('pedidos', 'pedidos.id = pedidos_linea.pedido_id')
            ->join('presupuestos', 'pedidos_linea.presupuesto_id = presupuestos.id')
            ->join('ordenes_trabajo', 'ordenes_trabajo.pedido_id = pedidos.id')
            ->where('ordenes_trabajo.id', $ot_id)
            ->where('presupuesto_direcciones.is_ferro_prototipo', 1)
            ->where("
                NOT EXISTS (
                    SELECT 1
                    FROM envios e
                    INNER JOIN envios_lineas el ON el.envio_id = e.id
                    WHERE e.tipo_envio = 'ferro_prototipo'
                    AND (el.pedido_id = pedidos.id OR el.presupuesto_id = presupuestos.id)
                )
            ", null, false) // <= Esta es la parte nueva, importante
            ->groupBy('presupuesto_direcciones.id')
            ->first();

        // Validación si no hay datos o no quedan unidades
        if (empty($datosEnvio)) {
            return [
                'status' => false,
                'message' => lang('Logistica.errors.noAddresses')
            ];
        }

        // Crear envío
        $EnvioModel = model('App\Models\Logistica\EnvioModel');
        $EnvioModel->set([
            'cliente_id' => $datosEnvio->cliente_id,
            'att' => $datosEnvio->att,
            'direccion' => $datosEnvio->direccion,
            'ciudad' => $datosEnvio->ciudad,
            'cp' => $datosEnvio->cp,
            'telefono' => $datosEnvio->telefono,
            'email' => $datosEnvio->email,
            'pais_id' => $datosEnvio->pais_id,
            'cantidad' => 1,
            'cajas' => 1,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
            'tipo_envio' => 'ferro_prototipo',
        ]);
        $EnvioModel->insert();
        $idEnvio = $EnvioModel->insertID();

        // Crear línea de envío
        $EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');
        $EnvioLineasModel->save([
            'envio_id' => $idEnvio,
            'pedido_id' => $datosEnvio->pedido_id,
            'unidades_envio' => 1,
            'unidades_total' => 1,
            'cajas' => 1,
            'unidades_cajas' => 1,
            'created_at' => date('Y-m-d H:i:s'),
            'updated_at' => date('Y-m-d H:i:s'),
            'created_by' => auth()->user()->id,
            'updated_by' => auth()->user()->id,
            'presupuesto_id' => (int) $datosEnvio->presupuesto_id
        ]);

        return [
            'status' => true,
            'data' => [
                'id_envio' => $idEnvio,
            ],
        ];
    }



    public static function finalizarEnvio($envio_id, $finalizar_ot = false)
    {
        // hay que comprobar que para todas las lineas de envio de este envio
        // se ha enviado toda la cantidad teniendo en cuenta otros envios
        $EnvioModel = model('App\Models\Logistica\EnvioModel');
        $EnvioLineasModel = model('App\Models\Logistica\EnvioLineaModel');

        $ots = [];

        $envio = $EnvioModel->find($envio_id);
        if (empty($envio)) {
            return [
                'status' => false,
                'message' => lang('Logistica.errors.noEnvio'),
            ];
        }
        $lineasEnvio = $EnvioLineasModel->where('envio_id', $envio_id)
            ->findAll();
        if (empty($lineasEnvio)) {
            return [
                'status' => false,
                'message' => lang('Logistica.errors.noEnvioLineas'),
            ];
        }

        foreach ($lineasEnvio as $linea) {

            $pedido = model('App\Models\Pedidos\PedidoModel')->find($linea->pedido_id);
            if (empty($pedido)) {
                return [
                    'status' => false,
                    'message' => lang('Logistica.errors.notFound'),
                ];
            }
            $cantidad_enviada = $EnvioLineasModel->select('SUM(envios_lineas.unidades_envio) as unidades_enviadas')
                ->where('envios_lineas.pedido_id', $linea->pedido_id)
                ->where('envios_lineas.envio_id !=', $envio_id)
                ->where('envios.finalizado', 1)
                ->join('envios', 'envios.id = envios_lineas.envio_id')
                ->get()->getResult();

            if (
                count($cantidad_enviada) == 0 ||
                empty($cantidad_enviada[0]->unidades_enviadas) ||
                $cantidad_enviada[0]->unidades_enviadas == null
            ) {
                $cantidad_enviada = 0;
            } else {
                $cantidad_enviada = $cantidad_enviada[0]->unidades_enviadas;
            }

            if ($envio->tipo_envio == 'ferro_prototipo') {
                $otModel = model('App\Models\OrdenTrabajo\OrdenTrabajoModel');
                $ot = $otModel->where('pedido_id', $linea->pedido_id)
                    ->first();
                $ps = (new ProductionService())->init($ot->id);
                $ps->updateOrdenTrabajoDate([
                    "name" => "ferro_en_cliente_at",
                    "ferro_en_cliente_at" => date('Y-m-d H:i:s')
                ]);
            } else {
                if ($cantidad_enviada + $linea->unidades_envio == $pedido->total_tirada) {
                    $otModel = model('App\Models\OrdenTrabajo\OrdenTrabajoModel');
                    $ot = $otModel->where('pedido_id', $linea->pedido_id)
                        ->first();
                    $ps = (new ProductionService())->init($ot->id);
                    $ps->updateOrdenTrabajoDate([
                        "name" => "envio_at",
                        "envio_at" => date('Y-m-d H:i:s')
                    ]);
                    if ($finalizar_ot) {
                        $ps->updateOrdenTrabajo(
                            [
                                "estado" => 'F'
                            ]
                        );
                        array_push($ots, $ot->id);
                    }
                }
            }

        }

        $EnvioModel->update($envio_id, ['finalizado' => 1]);

        $data_return = [
            'status' => true,
            'message' => lang('Logistica.success.finalizado'),
        ];

        if ($finalizar_ot) {
            $data_return['message'] = lang('Logistica.success.finalizadoOTs', [
                'ots' => implode(', ', $ots)
            ]);
            $data_return['ots'] = $ots;
        }
        return $data_return;
    }

    public static function generateEtiquetasTitulos($envio, $lineas, $printer, $cajas)
    {
        $data = [
            "printer" => $printer->name,
            "header" => [
                "_FORMAT" => "E:PEDIDO.ZPL",
                "_QUANTITY" => 1,
                "_PRINBTERNAME" => $printer->name,
                "_JOBNAME" => "LBL101"
            ],
        ];

        foreach ($lineas as $linea) {
            $data["labels"][] = [
                "cliente" => $envio->cliente,
                "titulo" => "[" . $linea->pedido_id . "] - " . $linea->titulo,
                "cantidad" => $linea->unidades_envio,
                "tirada" => $linea->unidades_total,
                "cajas" => $cajas,
                "ean" => null,
                "nombre" => $envio->att,
                "direccion" => $envio->direccion,
                "notas" => "",
                "refcliente" => $linea->refcliente,
                "npedido" => $linea->pedido_id
            ];
        }

        $servicioImpresora = new ImpresoraEtiquetaService();
        $xml = $servicioImpresora->createEtiqueta($data);
        if ($xml == null) {
            return [
                'status' => false,
                'message' => lang('Logistica.errors.noEtiquetas'),
            ];
        }
        $sk_environment = getenv('SK_ENVIRONMENT');
        if ($sk_environment == 'production') {

            $status = $servicioImpresora->sendToImpresoraEtiqueta("ETIQUETA", $xml, $printer);
            if ($status) {
                return [
                    'status' => true,
                    'message' => lang('Logistica.success.imprimirEtiquetas'),
                    'data' => $xml
                ];
            } else {
                return [
                    'status' => false,
                    'message' => lang('Logistica.errors.noEtiquetas'),
                ];
            }

        } else {
            return [
                'status' => true,
                'message' => lang('Logistica.success.imprimirEtiquetas'),
                'data' => $xml
            ];
        }
    }
}
