<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Unidade;
use App\Models\UserMonitoreo;
use App\Models\Puesto;
use App\Models\Misione;
use App\Models\MisioneItem;
use App\Models\Persona;
use Carbon\Carbon;
use DB;
use Illuminate\Support\Facades\Storage;

class UnidadController extends Controller
{

    /**
     * Identificación militar usada por mapa, resumen mensual y Control Diario.
     * Consulta el grado y la especialidad ACTIVOS registrados en Personal;
     * no presenta esos datos como grado histórico de la fecha del GPS.
     * Las subconsultas no multiplican registros de asignación/GPS.
     */
    private const NOMBRE_MILITAR_SQL = <<<'SQL'
TRIM(CONCAT_WS(' ',
    NULLIF((
        SELECT g.grad_abrev
        FROM persona_grados pg
        INNER JOIN grados g ON g.grad_cod = pg.pergrad_grad_cod
        WHERE pg.pergrad_per_cod = p.per_cod AND pg.pergrad_estado = 'A'
        ORDER BY pg.pergrad_grad_cod DESC
        LIMIT 1
    ), ''),
    NULLIF((
        SELECT e.espe_abrev
        FROM persona_especialidades pe
        INNER JOIN especialidades e ON e.espe_cod = pe.perespe_espe_cod
        WHERE pe.perespe_per_cod = p.per_cod AND pe.perespe_estado = 'A'
        ORDER BY pe.perespe_espe_cod DESC
        LIMIT 1
    ), ''),
    NULLIF(p.per_nombres, ''),
    NULLIF(p.per_apaterno, ''),
    NULLIF(p.per_amaterno, '')
))
SQL;

    private array $imageFields = [
        'uni_imagen',
        'uni_img',
        'imagen',
        'image',
        'foto',
        'archivo',
    ];

    private function getUnidadImageFile(Request $request)
    {
        foreach ($this->imageFields as $field) {
            if ($request->hasFile($field)) {
                return $request->file($field);
            }
        }

        return null;
    }

    private function storeUnidadImage(Request $request): ?string
    {
        $image = $this->getUnidadImageFile($request);

        if (!$image || !$image->isValid()) {
            return null;
        }

        $path = $image->store('unidades', 'public');

        return 'storage/' . $path;
    }

    private function deleteUnidadImage(?string $imagePath): void
    {
        if (!$imagePath) {
            return;
        }

        $path = preg_replace('/^\/?storage\//', '', $imagePath);

        if ($path && ($path !== $imagePath || str_starts_with($imagePath, 'unidades/'))) {
            Storage::disk('public')->delete($path);
        }
    }

    /**
     * Display a listing of the resource.
     */
    public function index(Request $request)
    {
        $search = $request->get("search");
        if (auth()->user()->roles[0]->name == 'Super-Admin' || auth()->user()->roles[0]->name == 'Admin-Dpto-III') {
            $unidades = Unidade::select('unidades.uni_cod', 'unidades.uni_detalle', 'unidades.uni_abrev', 'unidades.uni_codsup', 'unisup.uni_detalle as unisup', 'unidades.uni_fechareg', 'unidades.uni_geojson', 'unidades.uni_latitud', 'unidades.uni_longitud', 'unidades.uni_imagen', 'unidades.uni_estado')
                ->leftJoin('unidades as unisup', 'unisup.uni_cod', '=', 'unidades.uni_codsup')
                ->where(DB::raw("
                                        unidades.uni_detalle || ' ' ||
                                        unidades.uni_abrev || ' ' ||
                                        COALESCE(unisup.uni_detalle, '') || ' ' ||
                                        COALESCE(unisup.uni_abrev, '')
                                    "), 'ilike', "%{$search}%")
                ->whereNull('unidades.uni_codsup')
                ->orderBy("unidades.uni_cod", "desc")->get();
        } elseif (auth()->user()->roles[0]->name == 'G-3') {
            $unidades = Unidade::select('unidades.uni_cod', 'unidades.uni_detalle', 'unidades.uni_abrev', 'unidades.uni_codsup', 'unisup.uni_detalle as unisup', 'unidades.uni_fechareg', 'unidades.uni_geojson', 'unidades.uni_latitud', 'unidades.uni_longitud', 'unidades.uni_imagen', 'unidades.uni_estado')
                ->leftJoin('unidades as unisup', 'unisup.uni_cod', '=', 'unidades.uni_codsup')
                ->where(DB::raw("
                                        unidades.uni_detalle || ' ' ||
                                        unidades.uni_abrev || ' ' ||
                                        COALESCE(unisup.uni_detalle, '') || ' ' ||
                                        COALESCE(unisup.uni_abrev, '')
                                    "), 'ilike', "%{$search}%")
                //->whereNull('unidades.uni_codsup')
                ->where("unidades.uni_cod", auth()->user()->user_uni_cod)
                ->orderBy("unidades.uni_cod", "desc")->get();
        }

        return response()->json([
            "unidades" => $unidades
        ]);
    }
    /*NUEVOS REQUERIMIENTOS*/
    public function listunidadesdependientes(Request $request, $uni_cod)
    {
        $search = $request->get("search");

        $unidades = Unidade::select('unidades.uni_cod', 'unidades.uni_detalle', 'unidades.uni_abrev', 'unidades.uni_codsup', 'unisup.uni_detalle as unisup', 'unidades.uni_fechareg', 'unidades.uni_geojson', 'unidades.uni_latitud', 'unidades.uni_longitud', 'unidades.uni_imagen', 'unidades.uni_estado')
            ->leftJoin('unidades as unisup', 'unisup.uni_cod', '=', 'unidades.uni_codsup')
            ->where(DB::raw("
                                        unidades.uni_detalle || ' ' ||
                                        unidades.uni_abrev || ' ' ||
                                        COALESCE(unisup.uni_detalle, '') || ' ' ||
                                        COALESCE(unisup.uni_abrev, '')
                                    "), 'ilike', "%{$search}%")
            ->where('unidades.uni_codsup', $uni_cod)
            ->orderBy("unidades.uni_cod", "desc")->get();

        return response()->json([
            "unidades" => $unidades
        ]);
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        $is_uni_exists = Unidade::where("uni_detalle", $request->uni_detalle)->first();
        if ($is_uni_exists) {
            return response()->json([
                "message" => 403,
                "message_text" => "LA UNIDAD YA EXISTE"
            ]);
        }
        $imagePath = $this->storeUnidadImage($request);

        $unidade = Unidade::create([
            "uni_detalle" => $request->uni_detalle,
            "uni_abrev" => $request->uni_abrev,
            "uni_codsup" => $request->uni_codsup,
            "uni_geojson" => $request->uni_geojson,
            "uni_latitud" => $request->uni_latitud,
            "uni_longitud" => $request->uni_longitud,
            "uni_imagen" => $imagePath,
            "uni_estado" => "A",
            "uni_usuareg" => auth()->user()->id,
            "uni_fechareg" => Date("Y-m-d")
        ]);

        return response()->json([
            "message" => 200,
            "unidade" => $unidade
        ]);
    }

    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        $unidade = Unidade::where("uni_cod", $id)
            ->where("uni_estado", "A")
            ->first();

        if (!$unidade) {
            return response()->json([
                "message" => 404,
                "message_text" => "LA UNIDAD NO EXISTE"
            ], 404);
        }

        return response()->json([
            "message" => 200,
            "unidade" => $unidade
        ]);
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        $is_uni_exists = Unidade::where("uni_detalle", $request->uni_detalle)->where("uni_cod", "<>", $id)->first();
        if ($is_uni_exists) {
            return response()->json([
                "message" => 403,
                "message_text" => "LA UNIDAD YA EXISTE"
            ]);
        }
        $unidade = Unidade::where("uni_cod", $id)->first();
        if (!$unidade) {
            return response()->json([
                "message" => 404,
                "message_text" => "LA UNIDAD NO EXISTE"
            ], 404);
        }

        $imagePath = $this->storeUnidadImage($request);

        $updateData = [
            "uni_detalle" => $request->uni_detalle,
            "uni_abrev" => $request->uni_abrev,
            "uni_codsup" => $request->uni_codsup,
            "uni_geojson" => $request->uni_geojson,
            "uni_latitud" => $request->uni_latitud,
            "uni_longitud" => $request->uni_longitud,
            "uni_usuamod" => auth()->user()->id,
            "uni_fechamod" => Date("Y-m-d")
        ];

        if ($imagePath) {
            $this->deleteUnidadImage($unidade->uni_imagen);
            $updateData["uni_imagen"] = $imagePath;
        }

        $unidade->update($updateData);

        return response()->json([
            "message" => 200,
            "unidade" => $unidade
        ]);
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        $unidade = unidade::where("uni_cod", $id)->first();
        $unidade->uni_estado = "N";
        $unidade->uni_fechabaja = Date("Y-m-d");
        $unidade->uni_usuabaja = auth()->user()->id;
        $unidade->save();

        return response()->json([
            "message" => 200
        ]);
    }

    public function updateLogo(Request $request, string $id)
    {
        $request->validate([
            'logo' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
        ]);

        $unidade = Unidade::findOrFail($id);
        $imagePath = $this->storeUnidadImage($request);

        if (! $imagePath) {
            return response()->json([
                'message' => 'No se pudo procesar la imagen enviada.',
            ], 422);
        }

        $this->deleteUnidadImage($unidade->uni_imagen);
        $unidade->update([
            'uni_imagen' => $imagePath,
            'uni_usuamod' => auth()->id(),
            'uni_fechamod' => now()->toDateString(),
        ]);

        return response()->json([
            'message' => 200,
            'message_text' => 'Emblema actualizado correctamente.',
            'unidade' => $unidade->fresh(),
        ]);
    }

    public function toggleEstado(string $id)
    {
        $unidade = Unidade::findOrFail($id);
        $activating = $unidade->uni_estado !== 'A';

        $unidade->uni_estado = $activating ? 'A' : 'N';
        $unidade->uni_usuamod = auth()->id();
        $unidade->uni_fechamod = now()->toDateString();
        $unidade->uni_usuabaja = $activating ? null : auth()->id();
        $unidade->uni_fechabaja = $activating ? null : now()->toDateString();
        $unidade->save();

        return response()->json([
            'message' => 200,
            'message_text' => $activating
                ? 'Unidad activada correctamente.'
                : 'Unidad desactivada correctamente.',
            'unidade' => $unidade,
        ]);
    }

    public function getunidades(Request $request)
    {
        $uni_detalle = $request->searchunidade ?? '';
        //return response()->json([ "data" => $uni_detalle]);
        $unidades = Unidade::select("uni_cod", "uni_detalle")
            ->where('uni_detalle', 'ilike', "%{$uni_detalle}%")
            ->where("uni_estado", "A")->get();
        return response()->json($unidades);
    }

    public function mapaunisups()
    {
        if (auth()->user()->roles[0]->name == 'Super-Admin' || auth()->user()->roles[0]->name == 'Admin-Dpto-III') {
            $unidades = Unidade::select('uni_cod', 'uni_abrev', 'uni_geojson', 'uni_latitud', 'uni_longitud', 'uni_codsup', 'uni_imagen')
                ->where('uni_estado', 'A')
                ->get();
            $unisups = Unidade::select('uni_cod', 'uni_abrev', 'uni_geojson', 'uni_latitud', 'uni_longitud', 'uni_imagen')
                ->whereNull('uni_codsup')
                ->where('uni_estado', 'A')
                ->get();
        } elseif (auth()->user()->roles[0]->name == 'G-3') {
            $unidades = Unidade::select('uni_cod', 'uni_abrev', 'uni_geojson', 'uni_latitud', 'uni_longitud', 'uni_codsup', 'uni_imagen')
                ->where('uni_estado', 'A')
                ->where('uni_codsup', auth()->user()->user_uni_cod)
                ->get();

            $unisups = Unidade::select('uni_cod', 'uni_abrev', 'uni_geojson', 'uni_latitud', 'uni_longitud', 'uni_imagen')
                ->whereNull('uni_codsup')
                ->where('uni_estado', 'A')
                ->where('uni_cod', auth()->user()->user_uni_cod)
                ->get();
        } elseif (auth()->user()->roles[0]->name == 'P-3') {
            $unidades = Unidade::select('uni_cod', 'uni_abrev', 'uni_latitud', 'uni_longitud', 'uni_codsup', 'uni_imagen')
                ->where('uni_estado', 'A')
                ->where('uni_cod', auth()->user()->user_uni_cod)
                ->get();

            $unisups = Unidade::select('uni_cod', 'uni_abrev', 'uni_geojson', 'uni_latitud', 'uni_longitud', 'uni_imagen')
                //->whereNull('uni_codsup')
                ->where('uni_estado', 'A')
                ->where('uni_cod', auth()->user()->user_uni_cod)
                ->get();
        }


        return response()->json([
            "unisups" => $unisups,
            "unidades" => $unidades
        ]);
    }

    /*public function mapadivision()
    {
        $unisups = Unidade::select('uni_cod','uni_abrev','uni_geojson','uni_latitud','uni_longitud','uni_imagen')
                           ->whereNull('uni_codsup')
                           ->where('uni_cod',auth()->user()->user_uni_cod)
                           ->where('uni_estado','A')
                           ->get();

        $unidades = Unidade::select('uni_cod','uni_abrev','uni_latitud','uni_longitud','uni_codsup','uni_imagen')
                           ->where('uni_estado','A')
                           ->where('uni_codsup',auth()->user()->user_uni_cod)
                           ->get();
        return response()->json([
            "unisups" => $unisups,
            "unidades" => $unidades
        ]);
    }*/
    public function mapadivision(Request $request)
    {
        // Obtener el código de la unidad padre desde la petición
        $codigoPadre = $request->codigo_padre;

        // Si no hay código padre, devolver todas las unidades superiores (sin padre)
        if (empty($codigoPadre) || $codigoPadre == 'null' || $codigoPadre == 'undefined') {
            // Obtener todas las unidades que son padres (sin uni_codsup)
            $unisups = Unidade::select('uni_cod', 'uni_abrev', 'uni_geojson', 'uni_latitud', 'uni_longitud', 'uni_imagen')
                ->whereNull('uni_codsup')
                ->where('uni_estado', 'A')
                ->get();

            // No hay unidades hijas porque no hay padre seleccionado
            $unidades = collect();

            return response()->json([
                "success" => true,
                "unisups" => $unisups,
                "unidades" => $unidades,
                "codigo_padre" => null
            ]);
        }

        // Validar que sea numérico
        if (!is_numeric($codigoPadre)) {
            return response()->json([
                "success" => false,
                "message" => "El código de unidad debe ser numérico",
                "unisups" => [],
                "unidades" => []
            ], 400);
        }

        $codigoPadre = (int)$codigoPadre;

        // Obtener la unidad superior (la que se seleccionó como padre)
        $unisups = Unidade::select('uni_cod', 'uni_abrev', 'uni_geojson', 'uni_latitud', 'uni_longitud', 'uni_imagen')
            ->where('uni_cod', $codigoPadre)
            ->where('uni_estado', 'A')
            ->get();

        // Obtener las unidades hijas de la unidad padre seleccionada
        $unidades = Unidade::select('uni_cod', 'uni_abrev', 'uni_latitud', 'uni_longitud', 'uni_codsup', 'uni_imagen')
            ->where('uni_estado', 'A')
            ->where('uni_codsup', $codigoPadre)
            ->get();

        return response()->json([
            "success" => true,
            "unisups" => $unisups,
            "unidades" => $unidades,
            "codigo_padre" => $codigoPadre
        ]);
    }

    /*public function markers(Request $request)
    {
        $fecha = $request->fecha ?? now()->toDateString();

        $markes = UserMonitoreo::select(
                'umoni_uni_cod',
                'umoni_latitud',
                'umoni_longitud',
                'umoni_fechahora',
                DB::raw("CONCAT(grados.grad_abrev,' ',especialidades.espe_abrev,' ',personas.per_nombres,' ',personas.per_apaterno,' ',personas.per_amaterno) AS pesonal")
            )
            ->join('unidades','unidades.uni_cod','=','user_monitoreos.umoni_uni_cod')
            ->join('users','users.id','=','user_monitoreos.umoni_user_id')
            ->join('personas','personas.per_cod','=','users.user_per_cod')
            ->leftJoin('persona_grados AS pg', function($join){
                $join->on('pg.pergrad_per_cod','=','personas.per_cod')
                ->where('pg.pergrad_estado','=','A');
            })
            ->leftJoin('grados','grados.grad_cod','=','pg.pergrad_grad_cod')
            ->leftJoin('persona_especialidades AS pe', function($join){
                $join->on('pe.perespe_per_cod','=','personas.per_cod')
                ->where('pe.perespe_estado','=','A');
            })
            ->leftJoin('especialidades','especialidades.espe_cod','=','pe.perespe_espe_cod')
            ->whereDate('umoni_fechahora', $fecha)
            ->where('unidades.uni_codsup',auth()->user()->user_uni_cod)
            ->where('umoni_estado', 'A')
            ->orderBy('umoni_uni_cod')
            ->orderBy('umoni_fechahora')
            ->get();

        return response()->json([
            "markes" => $markes
        ]);
    }*/

    public function markers(Request $request)
    {
        $fecha = $request->fecha ?? now()->toDateString();
        $unidad = $request->unidad; // Nuevo parámetro

        $query = UserMonitoreo::select(
            'umoni_uni_cod',
            'umoni_latitud',
            'umoni_longitud',
            'umoni_fechahora',
            DB::raw("CONCAT(grados.grad_abrev,' ',especialidades.espe_abrev,' ',personas.per_nombres,' ',personas.per_apaterno,' ',personas.per_amaterno) AS pesonal")
        )
            ->join('unidades', 'unidades.uni_cod', '=', 'user_monitoreos.umoni_uni_cod')
            ->join('users', 'users.id', '=', 'user_monitoreos.umoni_user_id')
            ->join('personas', 'personas.per_cod', '=', 'users.user_per_cod')
            ->leftJoin('persona_grados AS pg', function ($join) {
                $join->on('pg.pergrad_per_cod', '=', 'personas.per_cod')
                    ->where('pg.pergrad_estado', '=', 'A');
            })
            ->leftJoin('grados', 'grados.grad_cod', '=', 'pg.pergrad_grad_cod')
            ->leftJoin('persona_especialidades AS pe', function ($join) {
                $join->on('pe.perespe_per_cod', '=', 'personas.per_cod')
                    ->where('pe.perespe_estado', '=', 'A');
            })
            ->leftJoin('especialidades', 'especialidades.espe_cod', '=', 'pe.perespe_espe_cod')
            ->whereDate('umoni_fechahora', $fecha)
            //->where('unidades.uni_codsup', auth()->user()->user_uni_cod)
            ->where('umoni_estado', 'A');

        // Aplicar filtro de unidad si se proporciona
        if ($unidad) {
            $query->where('umoni_uni_cod', $unidad);
        }

        $markes = $query->orderBy('umoni_uni_cod')
            ->orderBy('umoni_fechahora')
            ->get();

        return response()->json([
            "markes" => $markes
        ]);
    }

    public function markersunidad(Request $request)
    {
        $fecha = $request->fecha ?? now()->toDateString();

        $markes = UserMonitoreo::select(
            'umoni_uni_cod',
            'umoni_latitud',
            'umoni_longitud',
            'umoni_fechahora',
            DB::raw("CONCAT(grados.grad_abrev,' ',especialidades.espe_abrev,' ',personas.per_nombres,' ',personas.per_apaterno,' ',personas.per_amaterno) AS pesonal")
        )
            ->join('users', 'users.id', '=', 'user_monitoreos.umoni_user_id')
            ->join('personas', 'personas.per_cod', '=', 'users.user_per_cod')
            ->leftJoin('persona_grados AS pg', function ($join) {
                $join->on('pg.pergrad_per_cod', '=', 'personas.per_cod')
                    ->where('pg.pergrad_estado', '=', 'A');
            })
            ->leftJoin('grados', 'grados.grad_cod', '=', 'pg.pergrad_grad_cod')
            ->leftJoin('persona_especialidades AS pe', function ($join) {
                $join->on('pe.perespe_per_cod', '=', 'personas.per_cod')
                    ->where('pe.perespe_estado', '=', 'A');
            })
            ->leftJoin('especialidades', 'especialidades.espe_cod', '=', 'pe.perespe_espe_cod')
            ->whereDate('umoni_fechahora', $fecha)
            ->where('umoni_uni_cod', auth()->user()->user_uni_cod)
            ->where('umoni_estado', 'A')
            ->orderBy('umoni_uni_cod')
            ->orderBy('umoni_fechahora')
            ->get();

        return response()->json([
            "markes" => $markes
        ]);
    }

    /*************************/
    public function getUnidadesPadre(Request $request)
    {
        $search = $request->search ?? '';
        if (auth()->user()->roles[0]->name == 'G-3') {
            $query = Unidade::select("uni_cod", "uni_detalle", "uni_abrev")
                ->where('uni_estado', 'A')
                ->where('uni_cod', auth()->user()->user_uni_cod);
        } else {
            $query = Unidade::select("uni_cod", "uni_detalle", "uni_abrev")
                ->where('uni_estado', 'A')
                ->where(function ($q) {
                    $q->whereNull('uni_codsup')
                        ->orWhere('uni_codsup', 0);
                });
        }

        if ($search) {
            $query->where('uni_detalle', 'ilike', "%{$search}%");
        }

        $unidades = $query->orderBy('uni_detalle')->get();

        return response()->json([
            "success" => true,
            "data" => $unidades
        ]);
    }

    /**
     * Obtener unidades hijas de una unidad padre específica
     */
    public function getUnidadesHijas(Request $request)
    {
        $codigoPadre = $request->codigo_padre;
        $search = $request->search ?? '';

        if (!$codigoPadre) {
            return response()->json([
                "success" => false,
                "message" => "Se requiere el código de la unidad padre",
                "data" => []
            ]);
        }

        // Validar que el código padre sea numérico
        if (!is_numeric($codigoPadre)) {
            return response()->json([
                "success" => false,
                "message" => "El código de la unidad padre debe ser numérico",
                "data" => []
            ]);
        }

        $query = Unidade::select("uni_cod", "uni_detalle", "uni_abrev", "uni_codsup")
            ->where('uni_estado', 'A')
            ->where('uni_codsup', (int)$codigoPadre);

        if ($search) {
            $query->where('uni_detalle', 'ilike', "%{$search}%");
        }

        $unidades = $query->orderBy('uni_detalle')->get();

        return response()->json([
            "success" => true,
            "data" => $unidades
        ]);
    }

    /**
     * Obtener una unidad específica con sus datos completos
     */
    public function getUnidadById($codigo)
    {
        // Validar que el código sea numérico
        if (!is_numeric($codigo)) {
            return response()->json([
                "success" => false,
                "message" => "El código de la unidad debe ser numérico"
            ], 400);
        }

        $unidad = Unidade::select(
            "uni_cod",
            "uni_detalle",
            "uni_abrev",
            "uni_codsup",
            "uni_latitud",
            "uni_longitud",
            "uni_imagen"
        )->where('uni_cod', (int)$codigo)
            ->where('uni_estado', 'A')
            ->first();

        if (!$unidad) {
            return response()->json([
                "success" => false,
                "message" => "Unidad no encontrada"
            ], 404);
        }

        return response()->json([
            "success" => true,
            "data" => $unidad
        ]);
    }


    /**
     * Listar divisiones según los permisos del usuario.
     *
     * Administradores: todas las divisiones activas.
     * G-3: únicamente su división asignada.
     * Otros roles: acceso denegado.
     */
    public function divisiones()
    {
        $usuario = auth()->user();

        if (!$usuario) {
            return response()->json([
                'success' => false,
                'message' => 'Usuario no autenticado.',
            ], 401);
        }

        $roles = $usuario->getRoleNames()->all();

        $rolesGlobales = [
            'Administrador',
            'Super-Admin',
            'Admin-Dpto-III',
        ];

        $esAdministrador = count(
            array_intersect($roles, $rolesGlobales)
        ) > 0;

        $esG3 = in_array('G-3', $roles, true);

        // Impedir el acceso a roles no autorizados.
        if (!$esAdministrador && !$esG3) {
            return response()->json([
                'success' => false,
                'message' => 'No tiene permisos para consultar divisiones.',
            ], 403);
        }

        $consulta = Unidade::select(
            'uni_cod',
            'uni_detalle',
            'uni_abrev'
        )
            ->where('uni_estado', 'A')
            ->whereNull('uni_codsup');

        /*
    |--------------------------------------------------------------------------
    | RESTRICCIÓN PARA G-3
    |--------------------------------------------------------------------------
    |
    | El usuario G-3 solo puede consultar la división
    | registrada en su campo user_uni_cod.
    |
    | No se admiten divisiones ajenas.
    |--------------------------------------------------------------------------
    */
        if (!$esAdministrador) {
            $unidadUsuario = (int) $usuario->user_uni_cod;

            if ($unidadUsuario <= 0) {
                return response()->json([
                    'success' => false,
                    'message' => 'El usuario no tiene una división asignada.',
                ], 403);
            }

            $consulta->where('uni_cod', $unidadUsuario);
        }

        $divisiones = $consulta
            ->orderBy('uni_abrev', 'asc')
            ->get();

        return response()->json([
            'success' => true,
            'divisiones' => $divisiones,
        ]);
    }



    /**
     * Consultar unidades dependientes según los permisos.
     *
     * Administradores: acceso global.
     * G-3: únicamente unidades de su división.
     * Otros roles: acceso denegado.
     */
    public function unidependientes($uni_cod)
    {
        $usuario = auth()->user();

        if (!$usuario) {
            return response()->json([
                'success' => false,
                'message' => 'Usuario no autenticado.',
            ], 401);
        }

        $roles = $usuario->getRoleNames()->all();

        $rolesGlobales = [
            'Administrador',
            'Super-Admin',
            'Admin-Dpto-III',
        ];

        $esAdministrador = count(
            array_intersect($roles, $rolesGlobales)
        ) > 0;

        $esG3 = in_array('G-3', $roles, true);

        if (!$esAdministrador && !$esG3) {
            return response()->json([
                'success' => false,
                'message' => 'No autorizado para consultar estas unidades.',
            ], 403);
        }

        $divisionSolicitada = (int) $uni_cod;

        if ($divisionSolicitada <= 0) {
            return response()->json([
                'success' => false,
                'message' => 'Código de división no válido.',
            ], 422);
        }

        /*
    |--------------------------------------------------------------------------
    | CONTROL DE ACCESO PARA G-3
    |--------------------------------------------------------------------------
    |
    | G-3 solo puede consultar unidades dependientes
    | de la división registrada en su usuario.
    |--------------------------------------------------------------------------
    */
        if (!$esAdministrador) {
            $divisionUsuario = (int) $usuario->user_uni_cod;

            if (
                $divisionUsuario <= 0 ||
                $divisionSolicitada !== $divisionUsuario
            ) {
                return response()->json([
                    'success' => false,
                    'message' => 'No tiene acceso a esta división.',
                ], 403);
            }

            // Verificar que su unidad sea una división activa.
            $divisionValida = Unidade::where(
                'uni_cod',
                $divisionUsuario
            )
                ->where('uni_estado', 'A')
                ->whereNull('uni_codsup')
                ->exists();

            if (!$divisionValida) {
                return response()->json([
                    'success' => false,
                    'message' => 'No tiene una división activa autorizada.',
                ], 403);
            }
        }

        /*
    |--------------------------------------------------------------------------
    | CONSULTA DE UNIDADES DEPENDIENTES
    |--------------------------------------------------------------------------
    */
        $unidependientes = Unidade::select(
            'uni_cod',
            'uni_detalle',
            'uni_abrev'
        )
            ->where('uni_estado', 'A')
            ->where('uni_codsup', $divisionSolicitada)
            ->orderBy('uni_abrev', 'asc')
            ->get();

        return response()->json([
            'success' => true,
            'unidependientes' => $unidependientes,
        ]);
    }


    /*PUESTOS DE ACADA UNIDAD*/
    public function listpuestos(Request $request, $id)
    {
        $search = $request->get("search");

        $puestos = Puesto::select(
            'puesto_cod',
            'puesto_detalle',
            'puesto_tipo',
            'puesto_geojson',
            'puesto_fechareg'
        )
            ->where(DB::raw("puesto_detalle"), 'ilike', "%{$search}%")
            ->where("puesto_estado", "A")
            ->where("puesto_uni_cod", $id)
            ->orderBy("puesto_cod", "desc")
            ->get();

        return response()->json([
            "puestos" => $puestos
        ]);
    }

    /**
     * Store a newly created resource in storage.
     */
    public function storepuesto(Request $request)
    {
        $tiposPermitidos = [
            'PP.MM.',
            'PP.MM.AA.',
            'PP.SS.',
        ];

        if (
            !$request->puesto_tipo ||
            !in_array($request->puesto_tipo, $tiposPermitidos, true)
        ) {
            return response()->json([
                "message" => 422,
                "message_text" => "SELECCIONE UN TIPO DE PUESTO VÁLIDO"
            ], 422);
        }

        $is_puesto_exists = Puesto::where("puesto_detalle", $request->puesto_detalle)->first();

        if ($is_puesto_exists) {
            return response()->json([
                "message" => 403,
                "message_text" => "EL PUESTO YA EXISTE"
            ]);
        }

        $puesto = Puesto::create([
            "puesto_detalle" => $request->puesto_detalle,
            "puesto_tipo" => $request->puesto_tipo,
            "puesto_uni_cod" => $request->puesto_uni_cod,
            "puesto_geojson" => $request->puesto_geojson,
            "puesto_estado" => "A",
            "puesto_usuareg" => auth()->user()->id,
            "puesto_fechareg" => Date("Y-m-d")
        ]);

        return response()->json([
            "message" => 200,
            "puesto" => $puesto
        ]);
    }

    /**
     * Display the specified resource.
     */
    public function showpuesto(string $id)
    {
        $puesto = Puesto::where("puesto_cod", $id)
            ->where("puesto_estado", "A")
            ->first();

        if (!$puesto) {
            return response()->json([
                "message" => 404,
                "message_text" => "EL PUESTO NO EXISTE"
            ], 404);
        }

        return response()->json([
            "message" => 200,
            "puesto" => $puesto
        ]);
    }

    /**
     * Update the specified resource in storage.
     */
    public function updatepuesto(Request $request, string $id)
    {
        $tiposPermitidos = [
            'PP.MM.',
            'PP.MM.AA.',
            'PP.SS.',
        ];

        if (
            $request->has('puesto_tipo') &&
            $request->puesto_tipo !== null &&
            !in_array($request->puesto_tipo, $tiposPermitidos, true)
        ) {
            return response()->json([
                "message" => 422,
                "message_text" => "EL TIPO DE PUESTO NO ES VÁLIDO"
            ], 422);
        }

        $is_puesto_exists = Puesto::where("puesto_detalle", $request->puesto_detalle)
            ->where("puesto_cod", "<>", $id)
            ->first();

        if ($is_puesto_exists) {
            return response()->json([
                "message" => 403,
                "message_text" => "EL PUESTO YA EXISTE"
            ]);
        }

        $puesto = Puesto::where("puesto_cod", $id)->first();

        if (!$puesto) {
            return response()->json([
                "message" => 404,
                "message_text" => "EL PUESTO NO EXISTE"
            ], 404);
        }

        $updateData = [
            "puesto_detalle" => $request->puesto_detalle,
            //"puesto_uni_cod" => $request->puesto_uni_cod,
            "puesto_geojson" => $request->puesto_geojson,
            "puesto_usuamod" => auth()->user()->id,
            "puesto_fechamod" => Date("Y-m-d")
        ];

        /*
         * Los puestos antiguos pueden tener puesto_tipo = NULL.
         * Solo se actualiza el tipo cuando el Frontend lo envía.
         */
        if ($request->has('puesto_tipo')) {
            $updateData["puesto_tipo"] = $request->puesto_tipo;
        }

        $puesto->update($updateData);

        return response()->json([
            "message" => 200,
            "puesto" => $puesto
        ]);
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroypuesto(string $id)
    {
        $puesto = Puesto::where("puesto_cod", $id)->first();
        $puesto->puesto_estado = "N";
        $puesto->puesto_fechabaja = Date("Y-m-d");
        $puesto->puesto_usuabaja = auth()->user()->id;
        $puesto->save();

        return response()->json([
            "message" => 200
        ]);
    }


    /**
     * Obtener puestos de una unidad según los permisos del usuario.
     *
     * Administradores: acceso global.
     * G-3: su división y unidades directamente dependientes.
     * P-3: únicamente su propia unidad.
     */
    public function getpuestosunidad($uni_cod)
    {
        $usuario = auth()->user();

        if (!$usuario) {
            return response()->json([
                'message' => 'Usuario no autenticado.',
            ], 401);
        }

        $roles = $usuario->getRoleNames()->all();

        $rolesGlobales = [
            'Administrador',
            'Super-Admin',
            'Admin-Dpto-III',
        ];

        $esAdministrador = count(
            array_intersect($roles, $rolesGlobales)
        ) > 0;

        $esG3 = in_array('G-3', $roles, true);
        $esP3 = in_array('P-3', $roles, true);

        if (!$esAdministrador && !$esG3 && !$esP3) {
            return response()->json([
                'message' => 'No autorizado para consultar puestos.',
            ], 403);
        }

        $unidadSolicitada = filter_var(
            $uni_cod,
            FILTER_VALIDATE_INT,
            ['options' => ['min_range' => 1]]
        );

        if ($unidadSolicitada === false) {
            return response()->json([
                'message' => 'Código de unidad no válido.',
            ], 422);
        }

        // Verificar que la unidad exista y esté activa.
        $unidad = Unidade::select(
            'uni_cod',
            'uni_codsup'
        )
            ->where('uni_cod', $unidadSolicitada)
            ->where('uni_estado', 'A')
            ->first();

        if (!$unidad) {
            return response()->json([
                'message' => 'Unidad no encontrada.',
            ], 404);
        }

        /*
    |--------------------------------------------------------------------------
    | VERIFICAR ACCESO A LA UNIDAD
    |--------------------------------------------------------------------------
    */
        if (!$esAdministrador) {
            $unidadUsuario = (int) $usuario->user_uni_cod;

            if ($unidadUsuario <= 0) {
                return response()->json([
                    'message' => 'El usuario no tiene unidad asignada.',
                ], 403);
            }

            $permitido = false;

            if ($esG3) {
                // Su propia división o una unidad directamente dependiente.
                $permitido =
                    (int) $unidad->uni_cod === $unidadUsuario
                    || (int) $unidad->uni_codsup === $unidadUsuario;
            } elseif ($esP3) {
                // P-3 únicamente consulta los puestos de su unidad.
                $permitido =
                    (int) $unidad->uni_cod === $unidadUsuario;
            }

            if (!$permitido) {
                return response()->json([
                    'message' => 'No tiene acceso a los puestos de esta unidad.',
                ], 403);
            }
        }

        /*
    |--------------------------------------------------------------------------
    | OBTENER PUESTOS AUTORIZADOS
    |--------------------------------------------------------------------------
    */
        $puestos = Puesto::select(
            'puesto_cod',
            'puesto_detalle',
            'puesto_tipo'
        )
            ->where('puesto_uni_cod', $unidadSolicitada)
            ->where('puesto_estado', 'A')
            ->get();

        return response()->json([
            'message' => 200,
            'puestos' => $puestos,
        ]);
    }


    /*MISIONES POR PUESTOS*/

    /**
     * Obtener operaciones de un puesto según los permisos.
     *
     * Administradores: acceso global.
     * G-3: operaciones de su división y unidades dependientes.
     * P-3: operaciones de su propia unidad.
     */
    public function getmisionespuestos($puesto_cod)
    {
        $usuario = auth()->user();

        if (!$usuario) {
            return response()->json([
                'message' => 'Usuario no autenticado.',
            ], 401);
        }

        $roles = $usuario->getRoleNames()->all();

        $rolesGlobales = [
            'Administrador',
            'Super-Admin',
            'Admin-Dpto-III',
        ];

        $esAdministrador = count(
            array_intersect($roles, $rolesGlobales)
        ) > 0;

        $esG3 = in_array('G-3', $roles, true);
        $esP3 = in_array('P-3', $roles, true);

        if (!$esAdministrador && !$esG3 && !$esP3) {
            return response()->json([
                'message' => 'No autorizado para consultar operaciones.',
            ], 403);
        }

        $puestoSolicitado = filter_var(
            $puesto_cod,
            FILTER_VALIDATE_INT,
            ['options' => ['min_range' => 1]]
        );

        if ($puestoSolicitado === false) {
            return response()->json([
                'message' => 'Código de puesto no válido.',
            ], 422);
        }

        /*
    |--------------------------------------------------------------------------
    | VERIFICAR PUESTO Y UNIDAD
    |--------------------------------------------------------------------------
    */
        $puesto = Puesto::select(
            'puesto_cod',
            'puesto_uni_cod'
        )
            ->where('puesto_cod', $puestoSolicitado)
            ->where('puesto_estado', 'A')
            ->first();

        if (!$puesto) {
            return response()->json([
                'message' => 'Puesto no encontrado.',
            ], 404);
        }

        $unidad = Unidade::select(
            'uni_cod',
            'uni_codsup'
        )
            ->where('uni_cod', $puesto->puesto_uni_cod)
            ->where('uni_estado', 'A')
            ->first();

        if (!$unidad) {
            return response()->json([
                'message' => 'La unidad del puesto no está disponible.',
            ], 404);
        }

        /*
    |--------------------------------------------------------------------------
    | RESTRICCIÓN DE ACCESO POR ROL
    |--------------------------------------------------------------------------
    */
        if (!$esAdministrador) {
            $unidadUsuario = (int) $usuario->user_uni_cod;
            $unidadPuesto = (int) $unidad->uni_cod;
            $divisionSuperior = (int) $unidad->uni_codsup;

            if ($unidadUsuario <= 0) {
                return response()->json([
                    'message' => 'El usuario no tiene unidad asignada.',
                ], 403);
            }

            $permitido = false;

            if ($esG3) {
                $permitido = (
                    $unidadPuesto === $unidadUsuario
                    || $divisionSuperior === $unidadUsuario
                );
            } elseif ($esP3) {
                $permitido = (
                    $unidadPuesto === $unidadUsuario
                );
            }

            if (!$permitido) {
                return response()->json([
                    'message' => 'No tiene acceso a las operaciones de este puesto.',
                ], 403);
            }
        }

        /*
    |--------------------------------------------------------------------------
    | CONSULTAR OPERACIONES AUTORIZADAS
    |--------------------------------------------------------------------------
    */
        $misones = Misione::select(
            'mision_cod',
            'mision_descrip',
            'mision_fecha',
            'mision_fechafin',
            DB::raw("(
            SELECT COUNT(DISTINCT mi.mitem_user_id)
            FROM misione_items AS mi
            WHERE mi.mitem_mision_cod = misiones.mision_cod
              AND mi.mitem_estado = 'A'
        ) AS total_asignados")
        )
            ->where('mision_puesto_cod', $puestoSolicitado)
            ->where('mision_uni_cod', $unidad->uni_cod)
            ->where('mision_estado', 'A')
            ->get();

        return response()->json([
            'message' => 200,
            'misones' => $misones,
        ]);
    }


    public function getpersonalmision($mision_cod)
    {
        $personal = MisioneItem::select(
            "misione_items.mitem_cod",
            "misiones.mision_fecha",
            "misiones.mision_uni_cod",
            DB::raw("CONCAT(grados.grad_abrev,' ',especialidades.espe_abrev,' ',personas.per_nombres,' ',personas.per_apaterno,' ',personas.per_amaterno) AS persona")
        )
            ->join("misiones", "misiones.mision_cod", "=", "misione_items.mitem_mision_cod")
            ->join("users", "users.id", "=", "misione_items.mitem_user_id")
            ->join("personas", "personas.per_cod", "=", "users.user_per_cod")
            ->leftJoin('persona_grados AS pg', function ($join) {
                $join->on('pg.pergrad_per_cod', '=', 'personas.per_cod')
                    ->where('pg.pergrad_estado', '=', 'A');
            })
            ->leftJoin('grados', 'grados.grad_cod', '=', 'pg.pergrad_grad_cod')
            ->leftJoin('persona_especialidades AS pe', function ($join) {
                $join->on('pe.perespe_per_cod', '=', 'personas.per_cod')
                    ->where('pe.perespe_estado', '=', 'A');
            })
            ->leftJoin('especialidades', 'especialidades.espe_cod', '=', 'pe.perespe_espe_cod')
            ->leftJoin('armas', 'armas.arma_cod', '=', 'personas.per_arma_cod')
            ->where("misione_items.mitem_mision_cod", $mision_cod)
            ->where("misione_items.mitem_estado", "A")
            ->orderBy("misione_items.mitem_cod", "desc")->get();

        return response()->json([
            "message" => 200,
            "personal" => $personal
        ]);
    }

    /*public function getMarkersPorMision($mision_cod)
    {
        $fecha = request()->input('fecha');

        $markers = UserMonitoreo::select(
                'user_monitoreos.umoni_cod',
                'user_monitoreos.umoni_uni_cod',
                'user_monitoreos.umoni_latitud',
                'user_monitoreos.umoni_longitud',
                'user_monitoreos.umoni_fechahora',
                DB::raw("CONCAT(grados.grad_abrev,' ',especialidades.espe_abrev,' ',personas.per_nombres,' ',personas.per_apaterno,' ',personas.per_amaterno) AS personal"),
                DB::raw("(SELECT uni_detalle FROM unidades WHERE uni_cod = user_monitoreos.umoni_uni_cod) AS nombre_unidad")
            )
            ->join('users','users.id','=','user_monitoreos.umoni_user_id')
            ->join('personas','personas.per_cod','=','users.user_per_cod')
            ->leftJoin('persona_grados AS pg', function($join){
                $join->on('pg.pergrad_per_cod','=','personas.per_cod')
                ->where('pg.pergrad_estado','=','A');
            })
            ->leftJoin('grados','grados.grad_cod','=','pg.pergrad_grad_cod')
            ->leftJoin('persona_especialidades AS pe', function($join){
                $join->on('pe.perespe_per_cod','=','personas.per_cod')
                ->where('pe.perespe_estado','=','A');
            })
            ->leftJoin('especialidades','especialidades.espe_cod','=','pe.perespe_espe_cod')
            ->join('misione_items', function($join) use ($mision_cod) {
                $join->on('misione_items.mitem_user_id', '=', 'users.id')
                     ->where('misione_items.mitem_mision_cod', '=', $mision_cod)
                     ->where('misione_items.mitem_estado', '=', 'A');
            })
            ->whereDate('user_monitoreos.umoni_fechahora', $fecha)
            ->where('user_monitoreos.umoni_estado', 'A')
            ->orderBy('user_monitoreos.umoni_uni_cod')
            ->orderBy('user_monitoreos.umoni_fechahora')
            ->get();

        // Obtener total de asignados a la misión
        $totalAsignados = MisioneItem::where('mitem_mision_cod', $mision_cod)
            ->where('mitem_estado', 'A')
            ->count();

        // Obtener detalle de la misión
        $mision = Misione::select('mision_descrip', 'mision_puesto_cod')
            ->where('mision_cod', $mision_cod)
            ->first();

        return response()->json([
            "markers" => $markers,
            "total_asignados" => $totalAsignados,
            "mision_descrip" => $mision ? $mision->mision_descrip : null,
            "mision_puesto_cod" => $mision ? $mision->mision_puesto_cod : null,
            "fecha" => $fecha
        ]);
    }*/

    public function getMarkersPorMision(Request $request, $mision_cod)
    {
        /*
    |--------------------------------------------------------------------------
    | 1. OBTENER DATOS DE LA MISIÓN
    |--------------------------------------------------------------------------
    */
        $mision = Misione::select(
            'mision_cod',
            'mision_descrip',
            'mision_puesto_cod',
            'mision_uni_cod',
            'mision_fecha',
            'mision_fechafin'
        )
            ->where('mision_cod', $mision_cod)
            ->first();

        if (!$mision) {
            return response()->json([
                'message' => 'La misión solicitada no existe.'
            ], 404);
        }

        /*
        |--------------------------------------------------------------------------
        | SEGURIDAD: ACCESO AL MAPA GPS POR DIVISIÓN Y UNIDAD
        |--------------------------------------------------------------------------
        |
        | Administradores: acceso global.
        | G-3: su división y unidades directamente dependientes.
        | P-3: únicamente su unidad.
        |
        | La autorización se valida antes de consultar posiciones GPS.
        |--------------------------------------------------------------------------
        */

        $usuario = auth()->user();

        if (!$usuario) {
            return response()->json([
                'message' => 'Usuario no autenticado.',
            ], 401);
        }

        $roles = $usuario->getRoleNames()->all();

        $rolesGlobales = [
            'Administrador',
            'Super-Admin',
            'Admin-Dpto-III',
        ];

        $esAdministrador = count(
            array_intersect($roles, $rolesGlobales)
        ) > 0;

        $esG3 = in_array('G-3', $roles, true);
        $esP3 = in_array('P-3', $roles, true);

        if (!$esAdministrador && !$esG3 && !$esP3) {
            return response()->json([
                'message' => 'No autorizado para consultar el mapa GPS.',
            ], 403);
        }

        if (!$esAdministrador) {
            $unidadUsuario = (int) $usuario->user_uni_cod;
            $unidadOperacion = (int) $mision->mision_uni_cod;

            if ($unidadUsuario <= 0 || $unidadOperacion <= 0) {
                return response()->json([
                    'message' => 'No tiene acceso a esta operación.',
                ], 403);
            }

            $permitido = false;

            if ($esG3) {
                // Verificar que G-3 pertenezca a una división activa.
                $divisionValida = Unidade::query()
                    ->where('uni_cod', $unidadUsuario)
                    ->where('uni_estado', 'A')
                    ->whereNull('uni_codsup')
                    ->exists();

                if ($divisionValida) {
                    // División propia o unidad directamente dependiente.
                    $permitido = Unidade::query()
                        ->where('uni_cod', $unidadOperacion)
                        ->where('uni_estado', 'A')
                        ->where(function ($query) use ($unidadUsuario) {
                            $query
                                ->where('uni_cod', $unidadUsuario)
                                ->orWhere('uni_codsup', $unidadUsuario);
                        })
                        ->exists();
                }
            } elseif ($esP3) {
                // P-3 solamente puede acceder a su propia unidad.
                $permitido = Unidade::query()
                    ->where('uni_cod', $unidadOperacion)
                    ->where('uni_estado', 'A')
                    ->where('uni_cod', $unidadUsuario)
                    ->exists();
            }

            if (!$permitido) {
                return response()->json([
                    'message' => 'No tiene autorización para consultar el seguimiento GPS de esta operación.',
                ], 403);
            }
        }


        /*
    |--------------------------------------------------------------------------
    | 2. FILTRO OPCIONAL POR PERSONAL
    |--------------------------------------------------------------------------
    |
    | SIN user_id:
    |   muestra todo el personal como funciona actualmente.
    |
    | CON user_id:
    |   devuelve únicamente el recorrido del personal seleccionado.
    |
    */
        $userIdFiltro = null;

        if ($request->filled('user_id')) {

            $userIdSolicitado = $request->input('user_id');

            if (
                !is_numeric($userIdSolicitado)
                || (int) $userIdSolicitado <= 0
            ) {
                return response()->json([
                    'message' =>
                    'El identificador del personal seleccionado no es válido.'
                ], 422);
            }

            $userIdFiltro = (int) $userIdSolicitado;
        }


        /*
    |--------------------------------------------------------------------------
    | 3. VALIDAR FECHA
    |--------------------------------------------------------------------------
    */
        try {

            $fechaInicio = $mision->mision_fecha
                ? Carbon::parse($mision->mision_fecha)->startOfDay()
                : null;

            $fechaFin = $mision->mision_fechafin
                ? Carbon::parse($mision->mision_fechafin)->endOfDay()
                : Carbon::today()->endOfDay();


            if (
                $fechaInicio
                && $fechaFin->lt($fechaInicio)
            ) {
                $fechaFin =
                    $fechaInicio
                    ->copy()
                    ->endOfDay();
            }


            if ($request->filled('fecha')) {

                $fechaSolicitada =
                    $request->input('fecha');

                if (
                    !is_string($fechaSolicitada)
                    || !preg_match(
                        '/^\d{4}-\d{2}-\d{2}$/',
                        $fechaSolicitada
                    )
                ) {
                    throw new \InvalidArgumentException(
                        'Formato de fecha inválido.'
                    );
                }


                [
                    $anio,
                    $mes,
                    $dia
                ] = array_map(
                    'intval',
                    explode(
                        '-',
                        $fechaSolicitada
                    )
                );


                $fechaSeleccionada =
                    Carbon::createSafe(
                        $anio,
                        $mes,
                        $dia
                    )
                    ->startOfDay();
            } else {

                $fechaSeleccionada =
                    $fechaInicio
                    ? $fechaInicio->copy()
                    : Carbon::today()->startOfDay();
            }
        } catch (\Throwable $error) {

            return response()->json([
                'message' =>
                'La fecha enviada no tiene el formato válido YYYY-MM-DD.'
            ], 422);
        }


        if (
            $fechaInicio
            && $fechaSeleccionada->lt($fechaInicio)
        ) {
            return response()->json([
                'message' =>
                'La fecha seleccionada es anterior al inicio de la misión.',

                'fecha_minima' =>
                $fechaInicio->toDateString(),

                'fecha_maxima' =>
                $fechaFin->toDateString(),
            ], 422);
        }


        if (
            $fechaSeleccionada->gt($fechaFin)
        ) {
            return response()->json([
                'message' =>
                'La fecha seleccionada está fuera del periodo de la misión.',

                'fecha_minima' =>
                $fechaInicio?->toDateString(),

                'fecha_maxima' =>
                $fechaFin->toDateString(),
            ], 422);
        }


        $fecha =
            $fechaSeleccionada->toDateString();


        /*
    |--------------------------------------------------------------------------
    | 4. OBTENER TODO EL PERSONAL ASIGNADO
    |--------------------------------------------------------------------------
    |
    | Esta lista NO se filtra porque será utilizada para llenar el selector
    | del Frontend.
    |
    */
        $usuariosMisionTodos =
            MisioneItem::select(

                'misione_items.mitem_user_id',

                'personas.per_cod',

                DB::raw("
                TRIM(
                    CONCAT(
                        COALESCE(grados.grad_abrev, ''),
                        ' ',
                        COALESCE(especialidades.espe_abrev, ''),
                        ' ',
                        COALESCE(personas.per_nombres, ''),
                        ' ',
                        COALESCE(personas.per_apaterno, ''),
                        ' ',
                        COALESCE(personas.per_amaterno, '')
                    )
                ) AS personal_nombre
            ")
            )

            ->join(
                'users',
                'users.id',
                '=',
                'misione_items.mitem_user_id'
            )

            ->join(
                'personas',
                'personas.per_cod',
                '=',
                'users.user_per_cod'
            )

            ->leftJoin(
                'persona_grados AS pg',
                function ($join) {

                    $join->on(
                        'pg.pergrad_per_cod',
                        '=',
                        'personas.per_cod'
                    )

                        ->where(
                            'pg.pergrad_estado',
                            '=',
                            'A'
                        );
                }
            )

            ->leftJoin(
                'grados',
                'grados.grad_cod',
                '=',
                'pg.pergrad_grad_cod'
            )

            ->leftJoin(
                'persona_especialidades AS pe',
                function ($join) {

                    $join->on(
                        'pe.perespe_per_cod',
                        '=',
                        'personas.per_cod'
                    )

                        ->where(
                            'pe.perespe_estado',
                            '=',
                            'A'
                        );
                }
            )

            ->leftJoin(
                'especialidades',
                'especialidades.espe_cod',
                '=',
                'pe.perespe_espe_cod'
            )

            ->where(
                'misione_items.mitem_mision_cod',
                $mision_cod
            )

            ->where(
                'misione_items.mitem_estado',
                'A'
            )

            ->get()

            ->unique(
                'mitem_user_id'
            )

            ->values();


        /*
    |--------------------------------------------------------------------------
    | 5. VALIDAR PERSONAL SELECCIONADO
    |--------------------------------------------------------------------------
    */
        if ($userIdFiltro !== null) {

            $personalExisteEnMision =
                $usuariosMisionTodos->contains(
                    function ($usuario) use ($userIdFiltro) {

                        return
                            (int) $usuario->mitem_user_id
                            ===
                            $userIdFiltro;
                    }
                );


            if (!$personalExisteEnMision) {

                return response()->json([
                    'message' =>
                    'El personal seleccionado no se encuentra asignado a esta operación.'
                ], 422);
            }
        }


        /*
    |--------------------------------------------------------------------------
    | 6. LISTA DEL PERSONAL PARA EL SELECTOR
    |--------------------------------------------------------------------------
    */
        $personalAsignado =
            $usuariosMisionTodos
            ->map(
                function ($usuario) {

                    return [

                        'user_id' =>
                        (int) $usuario->mitem_user_id,

                        'per_cod' =>
                        (int) $usuario->per_cod,

                        'persona' =>
                        trim(
                            $usuario->personal_nombre
                        ),
                    ];
                }
            )

            ->values();


        /*
    |--------------------------------------------------------------------------
    | 7. PERSONAL QUE SE MOSTRARÁ EN EL MAPA
    |--------------------------------------------------------------------------
    */
        if ($userIdFiltro !== null) {

            $usuariosMision =
                $usuariosMisionTodos

                ->filter(
                    function ($usuario) use ($userIdFiltro) {

                        return
                            (int) $usuario->mitem_user_id
                            ===
                            $userIdFiltro;
                    }
                )

                ->values()

                ->keyBy(
                    'mitem_user_id'
                );
        } else {

            $usuariosMision =
                $usuariosMisionTodos
                ->keyBy(
                    'mitem_user_id'
                );
        }


        /*
    |--------------------------------------------------------------------------
    | 8. OBTENER MONITOREO GPS
    |--------------------------------------------------------------------------
    */
        $monitoreos =
            UserMonitoreo::select(

                'user_monitoreos.*',

                DB::raw("
                (
                    SELECT uni_detalle
                    FROM unidades
                    WHERE uni_cod = user_monitoreos.umoni_uni_cod
                ) AS nombre_unidad
            "),

                DB::raw("
                (
                    SELECT uni_abrev
                    FROM unidades
                    WHERE uni_cod = user_monitoreos.umoni_uni_cod
                ) AS abrev_unidad
            ")
            )

            ->whereIn(
                'user_monitoreos.umoni_user_id',
                $usuariosMision
                    ->keys()
                    ->toArray()
            )


            /*
            |--------------------------------------------------------------------------
            | FILTRAR GPS POR OPERACIÓN Y PUESTO
            |--------------------------------------------------------------------------
            |
            | Solo se muestran posiciones registradas
            | específicamente para esta operación y puesto.
            |--------------------------------------------------------------------------
            */
            ->where(
                'user_monitoreos.umoni_mision_cod',
                $mision->mision_cod
            )

            ->where(
                'user_monitoreos.umoni_puesto_cod',
                $mision->mision_puesto_cod
            )


            ->whereDate(
                'user_monitoreos.umoni_fechahora',
                $fecha
            )

            ->where(
                'user_monitoreos.umoni_estado',
                'A'
            )

            ->orderBy(
                'user_monitoreos.umoni_fechahora'
            )

            ->get()

            ->groupBy(
                'umoni_user_id'
            );


        /*
    |--------------------------------------------------------------------------
    | 9. CONSTRUIR MARCADORES
    |--------------------------------------------------------------------------
    */
        $markers = [];


        foreach (
            $usuariosMision
            as $userId => $usuario
        ) {

            $monitoreosUser =
                $monitoreos->get(
                    $userId,
                    collect()
                );


            /*
        |--------------------------------------------------------------------------
        | PERSONAL SIN POSICIONES
        |--------------------------------------------------------------------------
        */
            if ($monitoreosUser->isEmpty()) {

                $markers[] =
                    (object) [

                        'umoni_cod' =>
                        null,

                        'umoni_user_id' =>
                        (int) $userId,

                        'umoni_uni_cod' =>
                        null,

                        'umoni_latitud' =>
                        null,

                        'umoni_longitud' =>
                        null,

                        'umoni_fechahora' =>
                        $fecha,

                        'personal' =>
                        trim(
                            $usuario->personal_nombre
                        ),

                        'nombre_unidad' =>
                        'Sin ubicación',

                        'abrev_unidad' =>
                        'N/A',

                        'tiene_monitoreo' =>
                        false,
                    ];
            } else {

                /*
            |--------------------------------------------------------------------------
            | PERSONAL CON POSICIONES
            |--------------------------------------------------------------------------
            */
                foreach (
                    $monitoreosUser
                    as $monitoreo
                ) {

                    $markers[] =
                        (object) [

                            'umoni_cod' =>
                            $monitoreo->umoni_cod,

                            'umoni_user_id' =>
                            (int) $userId,

                            'umoni_uni_cod' =>
                            $monitoreo->umoni_uni_cod,

                            'umoni_latitud' =>
                            $monitoreo->umoni_latitud,

                            'umoni_longitud' =>
                            $monitoreo->umoni_longitud,

                            'umoni_fechahora' =>
                            $monitoreo->umoni_fechahora,

                            'personal' =>
                            trim(
                                $usuario->personal_nombre
                            ),

                            'nombre_unidad' =>
                            $monitoreo->nombre_unidad
                                ??
                                'Unidad desconocida',

                            'abrev_unidad' =>
                            $monitoreo->abrev_unidad
                                ??
                                'N/A',

                            'tiene_monitoreo' =>
                            true,
                        ];
                }
            }
        }


        /*
    |--------------------------------------------------------------------------
    | 10. DATOS DE LA UNIDAD
    |--------------------------------------------------------------------------
    */
        $unidadInfo = null;
        $divisionInfo = null;


        if ($mision->mision_uni_cod) {

            $unidadInfo =
                Unidade::select(
                    'uni_cod',
                    'uni_detalle',
                    'uni_abrev',
                    'uni_codsup'
                )

                ->where(
                    'uni_cod',
                    $mision->mision_uni_cod
                )

                ->first();


            /*
        |--------------------------------------------------------------------------
        | DIVISIÓN SUPERIOR
        |--------------------------------------------------------------------------
        */
            if ($unidadInfo) {

                if ($unidadInfo->uni_codsup) {

                    $divisionInfo =
                        Unidade::select(
                            'uni_cod',
                            'uni_detalle',
                            'uni_abrev'
                        )

                        ->where(
                            'uni_cod',
                            $unidadInfo->uni_codsup
                        )

                        ->first();
                } else {

                    /*
                 * Si la misión estuviera asignada directamente a una División.
                 */
                    $divisionInfo =
                        (object) [

                            'uni_cod' =>
                            $unidadInfo->uni_cod,

                            'uni_detalle' =>
                            $unidadInfo->uni_detalle,

                            'uni_abrev' =>
                            $unidadInfo->uni_abrev,
                        ];
                }
            }
        }


        /*
    |--------------------------------------------------------------------------
    | 11. DATOS DEL PUESTO
    |--------------------------------------------------------------------------
    */
        $puestoInfo = null;


        if ($mision->mision_puesto_cod) {

            $puestoInfo =
                Puesto::select(
                    'puesto_cod',
                    'puesto_detalle',
                    'puesto_tipo',
                    'puesto_geojson'
                )

                ->where(
                    'puesto_cod',
                    $mision->mision_puesto_cod
                )

                ->first();
        }

        /*
|--------------------------------------------------------------------------
| VALIDAR POSICIONES GPS CONTRA EL PERÍMETRO DEL PUESTO
|--------------------------------------------------------------------------
|
| Solo evalúa GPS operativo vinculado.
| No interpreta registros históricos sin vínculo.
| No genera todavía alertas de incumplimiento.
|--------------------------------------------------------------------------
*/

        foreach ($markers as $marker) {

            if ($marker->tiene_monitoreo ?? false) {

                $evaluacion =
                    \App\Services\ValidadorPerimetroPuesto::evaluar(
                        $puestoInfo?->puesto_geojson,
                        $marker->umoni_latitud,
                        $marker->umoni_longitud,
                        50.0
                    );
            } else {

                $evaluacion = [
                    'estado' => 'NO_VERIFICABLE',
                    'distancia_limite_m' => null,
                    'motivo' => 'sin_gps_operativo_vinculado',
                ];
            }

            $marker->estado_geografico =
                $evaluacion['estado'];

            $marker->distancia_limite_m =
                $evaluacion['distancia_limite_m'];

            $marker->motivo_geografico =
                $evaluacion['motivo'];
        }
        /*
    |--------------------------------------------------------------------------
    | 12. PERSONAL SELECCIONADO
    |--------------------------------------------------------------------------
    */
        $personalSeleccionado = null;


        if ($userIdFiltro !== null) {

            $personalSeleccionado =
                $personalAsignado->first(
                    function ($usuario) use ($userIdFiltro) {

                        return
                            (int) $usuario['user_id']
                            ===
                            $userIdFiltro;
                    }
                );
        }


        /*
    |--------------------------------------------------------------------------
    | 13. RESPUESTA
    |--------------------------------------------------------------------------
    */
        return response()->json([

            'message' => 200,


            /*
        |--------------------------------------------------------------------------
        | MARCADORES
        |--------------------------------------------------------------------------
        */
            'markers' =>
            $markers,


            /*
        |--------------------------------------------------------------------------
        | PERSONAL PARA EL SELECTOR
        |--------------------------------------------------------------------------
        */
            'personal_asignado' =>
            $personalAsignado,


            /*
        |--------------------------------------------------------------------------
        | FILTRO
        |--------------------------------------------------------------------------
        */
            'user_id_filtrado' =>
            $userIdFiltro,

            'filtro_personal_activo' =>
            $userIdFiltro !== null,

            'personal_seleccionado' =>
            $personalSeleccionado,


            /*
        |--------------------------------------------------------------------------
        | TOTALES
        |--------------------------------------------------------------------------
        */
            'total_asignados' =>
            $usuariosMisionTodos->count(),

            'total_filtrados' =>
            $usuariosMision->count(),

            'total_con_monitoreo' =>
            $monitoreos->count(),


            /*
        |--------------------------------------------------------------------------
        | MISIÓN
        |--------------------------------------------------------------------------
        */
            'mision_cod' =>
            (int) $mision_cod,

            'mision_descrip' =>
            $mision->mision_descrip,

            'mision_uni_cod' =>
            $mision->mision_uni_cod,

            'mision_puesto_cod' =>
            $mision->mision_puesto_cod,

            'mision_fecha' =>
            $mision->mision_fecha,

            'mision_fechafin' =>
            $mision->mision_fechafin,


            /*
        |--------------------------------------------------------------------------
        | PUESTO
        |--------------------------------------------------------------------------
        */
            'puesto_detalle' =>
            $puestoInfo
                ? $puestoInfo->puesto_detalle
                : null,

            'puesto_tipo' =>
            $puestoInfo
                ? $puestoInfo->puesto_tipo
                : null,


            /*
        |--------------------------------------------------------------------------
        | UNIDAD
        |--------------------------------------------------------------------------
        */
            'unidad' =>
            $unidadInfo
                ? [
                    'uni_cod' =>
                    (int) $unidadInfo->uni_cod,

                    'uni_detalle' =>
                    $unidadInfo->uni_detalle,

                    'uni_abrev' =>
                    $unidadInfo->uni_abrev,
                ]
                : null,

            'unidad_cod' =>
            $unidadInfo
                ? (int) $unidadInfo->uni_cod
                : null,

            'unidad_detalle' =>
            $unidadInfo
                ? $unidadInfo->uni_detalle
                : null,

            'unidad_abrev' =>
            $unidadInfo
                ? $unidadInfo->uni_abrev
                : null,


            /*
        |--------------------------------------------------------------------------
        | DIVISIÓN
        |--------------------------------------------------------------------------
        */
            'division' =>
            $divisionInfo
                ? [
                    'uni_cod' =>
                    (int) $divisionInfo->uni_cod,

                    'uni_detalle' =>
                    $divisionInfo->uni_detalle,

                    'uni_abrev' =>
                    $divisionInfo->uni_abrev,
                ]
                : null,

            'division_cod' =>
            $divisionInfo
                ? (int) $divisionInfo->uni_cod
                : null,

            'division_detalle' =>
            $divisionInfo
                ? $divisionInfo->uni_detalle
                : null,

            'division_abrev' =>
            $divisionInfo
                ? $divisionInfo->uni_abrev
                : null,


            /*
        |--------------------------------------------------------------------------
        | FECHAS
        |--------------------------------------------------------------------------
        */
            'fecha_minima' =>
            $fechaInicio?->toDateString(),

            'fecha_maxima' =>
            $fechaFin->toDateString(),

            'fecha' =>
            $fecha,
        ]);
    }

    /**
     * Historial GPS de un militar en una unidad autorizada.
     *
     * IMPORTANTE:
     * - Es una consulta de solo lectura.
     * - Incluye exclusivamente GPS sin vinculación COMPLETA de operación/puesto.
     * - NO acredita participación en la operación solicitada ni presencia en el puesto.
     * - Se exige una operación como contexto de autorización, no como vínculo GPS.
     * - La unidad registrada en cada GPS debe coincidir con la unidad de ese contexto.
     * - No altera la consulta operativa getMarkersPorMision ni el control diario.
     *
     * GET markers-historicos-personal/{mision_cod}?user_id=12&fecha=2026-09-04
     */
    public function getHistorialGpsPersonal(Request $request, $mision_cod)
    {
        $usuario = auth()->user();

        if (!$usuario) {
            return response()->json(['message' => 'Usuario no autenticado.'], 401);
        }

        $datos = $request->validate([
            'user_id' => ['required', 'integer', 'min:1'],
            'fecha' => ['required', 'date_format:Y-m-d'],
        ]);

        $userId = (int) $datos['user_id'];
        $fecha = $datos['fecha'];
        $zonaHoraria = 'America/La_Paz';

        if ($fecha > Carbon::today($zonaHoraria)->toDateString()) {
            return response()->json([
                'message' => 'No es posible consultar una fecha futura.',
            ], 422);
        }

        $mision = Misione::query()
            ->select('mision_cod', 'mision_descrip', 'mision_uni_cod', 'mision_puesto_cod')
            ->where('mision_cod', $mision_cod)
            ->first();

        if (!$mision) {
            return response()->json(['message' => 'La operación no existe.'], 404);
        }

        $unidadCod = (int) $mision->mision_uni_cod;

        if ($unidadCod <= 0) {
            return response()->json([
                'message' => 'La operación no tiene una unidad asociada.',
            ], 422);
        }

        // Validación de rol y alcance equivalente al monitoreo operativo.
        $roles = $usuario->getRoleNames()->all();
        $rolesGlobales = ['Administrador', 'Super-Admin', 'Admin-Dpto-III'];
        $esAdministrador = count(array_intersect($roles, $rolesGlobales)) > 0;
        $esG3 = in_array('G-3', $roles, true);
        $esP3 = in_array('P-3', $roles, true);

        if (!$esAdministrador && !$esG3 && !$esP3) {
            return response()->json([
                'message' => 'No autorizado para consultar el historial GPS.',
            ], 403);
        }

        if (!$esAdministrador) {
            $unidadUsuario = (int) $usuario->user_uni_cod;
            $permitido = false;

            if ($unidadUsuario > 0 && $esG3) {
                $divisionValida = Unidade::query()
                    ->where('uni_cod', $unidadUsuario)
                    ->where('uni_estado', 'A')
                    ->whereNull('uni_codsup')
                    ->exists();

                if ($divisionValida) {
                    $permitido = Unidade::query()
                        ->where('uni_cod', $unidadCod)
                        ->where('uni_estado', 'A')
                        ->where(function ($query) use ($unidadUsuario) {
                            $query->where('uni_cod', $unidadUsuario)
                                ->orWhere('uni_codsup', $unidadUsuario);
                        })
                        ->exists();
                }
            } elseif ($unidadUsuario > 0 && $esP3) {
                $permitido = Unidade::query()
                    ->where('uni_cod', $unidadCod)
                    ->where('uni_estado', 'A')
                    ->where('uni_cod', $unidadUsuario)
                    ->exists();
            }

            if (!$permitido) {
                return response()->json([
                    'message' => 'No tiene permiso para consultar el historial de esta unidad.',
                ], 403);
            }
        }

        // La persona debe pertenecer al listado de la operación consultada.
        // Su asociación administrativa NO se interpreta como prueba de que
        // estuvo asignada a esa operación en la fecha histórica solicitada.
        $personalEnOperacion = MisioneItem::query()
            ->where('mitem_mision_cod', $mision->mision_cod)
            ->where('mitem_user_id', $userId)
            ->exists();

        if (!$personalEnOperacion) {
            return response()->json([
                'message' => 'El personal no está relacionado con la operación consultada.',
            ], 403);
        }

        $unidad = Unidade::query()
            ->select('uni_cod', 'uni_detalle', 'uni_abrev')
            ->where('uni_cod', $unidadCod)
            ->first();

        // Fechas por intervalo semiabierto; evita WHERE DATE sobre la columna.
        $inicio = Carbon::createFromFormat('!Y-m-d', $fecha, $zonaHoraria)
            ->startOfDay();
        $finExclusivo = $inicio->copy()->addDay();

        // Límite protector: un día con GPS continuo puede tener miles de puntos.
        // Se informa si hay más de 10000 en vez de afirmar que son todos.
        $maximo = 10000;
        $gps = UserMonitoreo::query()
            ->select(
                'umoni_cod',
                'umoni_user_id',
                'umoni_uni_cod',
                'umoni_latitud',
                'umoni_longitud',
                'umoni_fechahora'
            )
            ->where('umoni_user_id', $userId)
            ->where('umoni_uni_cod', $unidadCod)
            ->where('umoni_estado', 'A')
            ->whereNotNull('umoni_latitud')
            ->whereNotNull('umoni_longitud')
            ->where(function ($query) {
                $query->whereNull('umoni_mision_cod')
                    ->orWhereNull('umoni_puesto_cod');
            })
            ->where('umoni_fechahora', '>=', $inicio->format('Y-m-d H:i:s'))
            ->where('umoni_fechahora', '<', $finExclusivo->format('Y-m-d H:i:s'))
            ->orderBy('umoni_fechahora')
            ->orderBy('umoni_cod')
            ->limit($maximo + 1)
            ->get();

        $truncado = $gps->count() > $maximo;
        $gps = $gps->take($maximo);

        $nombrePersonal = DB::table('users as u')
            ->join('personas as p', 'p.per_cod', '=', 'u.user_per_cod')
            ->where('u.id', $userId)
            ->selectRaw(self::NOMBRE_MILITAR_SQL . ' AS nombre')
            ->value('nombre');

        $nombrePersonal = trim((string) $nombrePersonal);

        $markers = $gps->map(function ($posicion) use ($nombrePersonal, $unidad) {
            return [
                'umoni_cod' => $posicion->umoni_cod,
                'umoni_user_id' => (int) $posicion->umoni_user_id,
                'umoni_uni_cod' => (int) $posicion->umoni_uni_cod,
                'umoni_latitud' => $posicion->umoni_latitud,
                'umoni_longitud' => $posicion->umoni_longitud,
                'umoni_fechahora' => $posicion->umoni_fechahora,
                'personal' => $nombrePersonal !== '' ? $nombrePersonal : 'Personal ' . $posicion->umoni_user_id,
                'nombre_unidad' => $unidad?->uni_detalle ?? 'Unidad no disponible',
                'abrev_unidad' => $unidad?->uni_abrev ?? 'N/A',
                'tiene_monitoreo' => true,
                'vinculacion_operativa_verificada' => false,
                'origen_gps' => 'historico_sin_vinculacion',
            ];
        })->values();

        return response()->json([
            'message' => 200,
            'tipo_consulta' => 'historial_personal_sin_vinculacion',
            'fecha' => $fecha,
            'user_id' => $userId,
            'unidad_cod' => $unidadCod,
            'unidad_detalle' => $unidad?->uni_detalle,
            'mision_cod_contexto' => (int) $mision->mision_cod,
            'mision_descrip_contexto' => $mision->mision_descrip,
            'total_posiciones_historicas' => $markers->count(),
            'truncado' => $truncado,
            'limite' => $maximo,
            'markers' => $markers,
            'advertencia' => 'Recorrido histórico del personal en la unidad. Estos GPS no tienen una vinculación completa con operación y puesto; NO acreditan presencia ni asignación en la operación del contexto.',
        ]);
    }


    /**
     * Resumen mensual de actividad GPS registrada por personal de la operación.
     * La operación es contexto de autorización: GPS históricos sin vínculo
     * NUNCA se consideran prueba de presencia o cumplimiento de esa operación.
     * Solo lectura, datos agrupados, sin coordenadas individuales.
     */
    public function resumenMesesGps(Request $request, $mision_cod)
    {
        $usuario = auth()->user();
        if (!$usuario) {
            return response()->json(['message' => 'No autenticado.'], 401);
        }

        $mision = Misione::select(
            'mision_cod',
            'mision_descrip',
            'mision_uni_cod',
            'mision_puesto_cod',
            'mision_fecha',
            'mision_fechafin'
        )
            ->where('mision_cod', $mision_cod)->first();
        if (!$mision) {
            return response()->json(['message' => 'Operación no encontrada.'], 404);
        }
        if (!$mision->mision_fecha || !$mision->mision_puesto_cod || !$mision->mision_uni_cod) {
            return response()->json(['message' => 'Faltan datos de período, puesto o unidad.'], 422);
        }

        $roles = $usuario->getRoleNames()->all();
        $globales = ['Administrador', 'Super-Admin', 'Admin-Dpto-III'];
        $esGlobal = count(array_intersect($roles, $globales)) > 0;
        $esG3 = in_array('G-3', $roles, true);
        $esP3 = in_array('P-3', $roles, true);
        if (!$esGlobal && !$esG3 && !$esP3) {
            return response()->json(['message' => 'No autorizado.'], 403);
        }

        $unidad = (int) $mision->mision_uni_cod;
        if (!$esGlobal) {
            $unidadUsuario = (int) $usuario->user_uni_cod;
            $permitido = false;
            if ($esG3 && $unidadUsuario > 0) {
                $divisionValida = Unidade::where('uni_cod', $unidadUsuario)
                    ->where('uni_estado', 'A')->whereNull('uni_codsup')->exists();
                if ($divisionValida) {
                    $permitido = Unidade::where('uni_cod', $unidad)
                        ->where('uni_estado', 'A')
                        ->where(function ($q) use ($unidadUsuario) {
                            $q->where('uni_cod', $unidadUsuario)
                                ->orWhere('uni_codsup', $unidadUsuario);
                        })->exists();
                }
            } elseif ($esP3 && $unidadUsuario > 0) {
                $permitido = Unidade::where('uni_cod', $unidad)
                    ->where('uni_estado', 'A')->where('uni_cod', $unidadUsuario)->exists();
            }
            if (!$permitido) {
                return response()->json(['message' => 'Unidad fuera de su alcance.'], 403);
            }
        }

        $zona = 'America/La_Paz';
        $inicio = Carbon::parse($mision->mision_fecha, $zona)->startOfDay();
        $fin = $mision->mision_fechafin
            ? Carbon::parse($mision->mision_fechafin, $zona)->startOfDay()
            : Carbon::today($zona)->startOfDay();
        if ($fin->lt($inicio)) {
            return response()->json(['message' => 'Período de operación inválido.'], 422);
        }
        // No se consulta GPS del futuro, aunque la operación tenga una fecha final posterior.
        $hastaGps = $fin->copy();
        if ($hastaGps->gt(Carbon::today($zona))) $hastaGps = Carbon::today($zona);

        $personal = MisioneItem::query()
            ->join('users AS u', 'u.id', '=', 'misione_items.mitem_user_id')
            ->leftJoin('personas AS p', 'p.per_cod', '=', 'u.user_per_cod')
            ->where('misione_items.mitem_mision_cod', $mision->mision_cod)
            ->select('misione_items.mitem_user_id')
            ->selectRaw(self::NOMBRE_MILITAR_SQL . ' AS nombre')
            ->get()->unique('mitem_user_id')->values();
        $ids = $personal->pluck('mitem_user_id')->map(fn($id) => (int) $id)
            ->filter(fn($id) => $id > 0)->values()->all();


        // Incluir GPS histórico de la MISMA unidad y del personal de esta
        // operación, incluso antes de su inicio. La asociación administrativa
        // NO vincula ese GPS histórico a esta operación ni a su puesto.
        $meses = collect();
        if (count($ids)) {
            $limiteFuturo = Carbon::today($zona)->addDay()->format('Y-m-d H:i:s');
            $desdeVinculado = $inicio->format('Y-m-d H:i:s');
            $hastaVinculado = $hastaGps->copy()->addDay()->format('Y-m-d H:i:s');

            $meses = UserMonitoreo::query()
                ->whereIn('umoni_user_id', $ids)
                ->where('umoni_uni_cod', $unidad)
                ->where('umoni_estado', 'A')
                ->whereNotNull('umoni_latitud')
                ->whereNotNull('umoni_longitud')
                ->where('umoni_fechahora', '<', $limiteFuturo)
                ->where(function ($q) use ($mision, $desdeVinculado, $hastaVinculado) {
                    $q->where(function ($v) use ($mision, $desdeVinculado, $hastaVinculado) {
                        $v->where('umoni_mision_cod', $mision->mision_cod)
                            ->where('umoni_puesto_cod', $mision->mision_puesto_cod)
                            ->where('umoni_fechahora', '>=', $desdeVinculado)
                            ->where('umoni_fechahora', '<', $hastaVinculado);
                    })->orWhereNull('umoni_mision_cod')
                        ->orWhereNull('umoni_puesto_cod');
                })
                ->select('umoni_user_id')
                ->selectRaw("TO_CHAR(umoni_fechahora, 'YYYY-MM') AS mes")
                ->selectRaw(
                    'SUM(CASE WHEN umoni_mision_cod = ? AND umoni_puesto_cod = ? AND umoni_fechahora >= ? AND umoni_fechahora < ? THEN 1 ELSE 0 END) AS vinculados',
                    [$mision->mision_cod, $mision->mision_puesto_cod, $desdeVinculado, $hastaVinculado]
                )
                ->selectRaw('SUM(CASE WHEN umoni_mision_cod IS NULL OR umoni_puesto_cod IS NULL THEN 1 ELSE 0 END) AS historicos')
                ->groupBy('umoni_user_id')
                ->groupByRaw("TO_CHAR(umoni_fechahora, 'YYYY-MM')")
                ->orderBy('mes')
                ->get()->map(fn($r) => [
                    'user_id' => (int) $r->umoni_user_id,
                    'mes' => $r->mes,
                    'vinculados' => (int) $r->vinculados,
                    'historicos' => (int) $r->historicos,
                ]);
        }

        // Rango de calendario: incorporar el primer y último mes histórico
        // disponible, sin cambiar el período oficial de la operación.
        $primerMesConGps = $meses->min('mes');
        $ultimoMesConGps = $meses->max('mes');
        $inicioCalendario = $inicio->copy();
        $finCalendario = $fin->copy();
        if ($primerMesConGps && $primerMesConGps < $inicioCalendario->format('Y-m')) {
            $inicioCalendario = Carbon::createFromFormat('!Y-m', $primerMesConGps, $zona)->startOfMonth();
        }
        if ($ultimoMesConGps && $ultimoMesConGps > $finCalendario->format('Y-m')) {
            $finCalendario = Carbon::createFromFormat('!Y-m', $ultimoMesConGps, $zona)->endOfMonth();
        }

        return response()->json([
            'mision_cod' => (int) $mision->mision_cod,
            'mision_descrip' => $mision->mision_descrip,
            'fecha_inicio' => $inicioCalendario->toDateString(),
            'fecha_fin' => $finCalendario->toDateString(),
            'fecha_inicio_operacion' => $inicio->toDateString(),
            'fecha_fin_operacion' => $fin->toDateString(),
            'personal' => $personal->map(fn($p) => [
                'user_id' => (int) $p->mitem_user_id,
                'nombre' => $p->nombre,
            ])->values(),
            'meses' => $meses->values(),
            'advertencia' => 'Los meses anteriores o posteriores a la operación muestran GPS histórico sin vínculo, no participación verificada en la misión.',
        ]);
    }


    /**
     * Control diario GPS por operación y puesto.
     *
     * Exclusivamente los registros que tengan ambos códigos y coincidan con
     * la operación seleccionada se consideran GPS verificable de la operación.
     * Los registros históricos sin vinculación se informan por separado;
     * nunca se atribuyen automáticamente a una operación o puesto.
     */
    public function controlMonitoreoDiario(Request $request, $mision_cod)
    {
        $usuario = auth()->user();

        if (!$usuario) {
            return response()->json(['message' => 'No autenticado.'], 401);
        }

        $rol = $usuario->getRoleNames()->first();
        $rolesGlobales = ['Administrador', 'Super-Admin', 'Admin-Dpto-III'];

        if (!in_array($rol, array_merge($rolesGlobales, ['G-3', 'P-3']), true)) {
            return response()->json(['message' => 'No autorizado.'], 403);
        }

        $request->validate([
            'user_id' => ['nullable', 'integer', 'min:1'],
            'mes' => ['nullable', 'date_format:Y-m'],
        ]);

        $mision = Misione::select(
            'mision_cod',
            'mision_descrip',
            'mision_uni_cod',
            'mision_puesto_cod',
            'mision_fecha',
            'mision_fechafin'
        )->find($mision_cod);

        if (!$mision) {
            return response()->json(['message' => 'La operación no existe.'], 404);
        }

        $unidadMision = (int) $mision->mision_uni_cod;
        $unidadUsuario = (int) $usuario->user_uni_cod;

        if (!in_array($rol, $rolesGlobales, true)) {
            $permitido = $unidadMision > 0 && $unidadUsuario > 0
                && ($unidadMision === $unidadUsuario
                    || ($rol === 'G-3' && Unidade::where('uni_cod', $unidadMision)
                        ->where('uni_codsup', $unidadUsuario)->exists()));

            if (!$permitido) {
                return response()->json([
                    'message' => 'La operación no pertenece a una unidad autorizada.',
                ], 403);
            }
        }

        // Nunca atribuir GPS a una operación sin un puesto identificado.
        if (!$mision->mision_puesto_cod) {
            return response()->json([
                'message' => 'La operación no tiene un puesto asignado.',
            ], 422);
        }

        if (!$mision->mision_fecha) {
            return response()->json([
                'message' => 'La operación no tiene fecha de inicio.',
            ], 422);
        }

        $zonaHoraria = 'America/La_Paz';
        $inicio = Carbon::parse($mision->mision_fecha, $zonaHoraria)->startOfDay();
        $fin = $mision->mision_fechafin
            ? Carbon::parse($mision->mision_fechafin, $zonaHoraria)->startOfDay()
            : Carbon::today($zonaHoraria)->startOfDay();
        $hoy = Carbon::today($zonaHoraria)->toDateString();
        $inicioOriginal = $inicio->copy();
        $finOriginal = $fin->copy();

        // Opción mensual para operaciones de cualquier duración.
        // Sin 'mes' se conserva la respuesta anterior y su límite de 367 días.
        if ($request->filled('mes')) {
            try {
                $mesInicio = Carbon::createFromFormat('!Y-m', $request->input('mes'), $zonaHoraria)->startOfMonth();
            } catch (\Throwable $e) {
                return response()->json(['message' => 'Mes no válido.'], 422);
            }
            $mesFin = $mesInicio->copy()->endOfMonth()->startOfDay();
            // Fuera del período oficial: mostrar exclusivamente GPS históricos
            // sin vínculo del personal/unidad, nunca incidencias operativas.
            if ($mesFin->lt($inicioOriginal) || $mesInicio->gt($finOriginal)) {
                if ($mesInicio->gt(Carbon::today($zonaHoraria))) {
                    return response()->json(['message' => 'No se puede consultar un mes futuro.'], 422);
                }
                $hastaMes = $mesFin->gt(Carbon::today($zonaHoraria))
                    ? Carbon::today($zonaHoraria)->startOfDay()
                    : $mesFin;
                return $this->controlHistoricoFueraDeOperacion($mision, $mesInicio, $hastaMes);
            }
            if ($mesInicio->gt($inicio)) $inicio = $mesInicio;
            if ($mesFin->lt($fin)) $fin = $mesFin;
        }

        if ($fin->lt($inicio)) {
            return response()->json([
                'message' => 'El período de la operación no es válido.',
            ], 422);
        }

        if ($inicio->diffInDays($fin) > 366) {
            return response()->json([
                'message' => 'El período supera 367 días; debe habilitarse una consulta por tramos.',
            ], 422);
        }

        $fechas = [];
        for ($dia = $inicio->copy(); $dia->lte($fin); $dia->addDay()) {
            $fechas[] = $dia->toDateString();
        }

        $asignaciones = MisioneItem::query()
            ->leftJoin('users AS u', 'u.id', '=', 'misione_items.mitem_user_id')
            ->leftJoin('personas AS p', 'p.per_cod', '=', 'u.user_per_cod')
            ->where('misione_items.mitem_mision_cod', $mision->mision_cod)
            ->whereNotNull('misione_items.mitem_user_id')
            ->when($request->filled('user_id'), function ($q) use ($request) {
                $q->where('misione_items.mitem_user_id', (int) $request->user_id);
            })
            ->select(
                'misione_items.mitem_user_id',
                'misione_items.mitem_estado',
                'misione_items.mitem_fechareg',
                'misione_items.mitem_fechabaja',
                DB::raw(self::NOMBRE_MILITAR_SQL . ' AS personal')
            )
            ->get()
            ->groupBy('mitem_user_id');

        $ids = $asignaciones->keys()
            ->filter(fn($id) => (int) $id > 0)
            ->map(fn($id) => (int) $id)
            ->values()
            ->all();

        $gpsPorPersonaYFecha = collect();
        $gpsHistoricoSinVinculo = collect();
        $primerGpsVinculadoPorUsuario = collect();

        if (count($ids) > 0) {
            $desde = $inicio->format('Y-m-d H:i:s');
            $hastaExclusivo = $fin->copy()->addDay()->format('Y-m-d H:i:s');

            // GPS de esta operación y de este puesto: únicos registros atribuibles.
            $gpsPorPersonaYFecha = UserMonitoreo::query()
                ->whereIn('umoni_user_id', $ids)
                ->where('umoni_uni_cod', $unidadMision)
                ->where('umoni_mision_cod', $mision->mision_cod)
                ->where('umoni_puesto_cod', $mision->mision_puesto_cod)
                ->where('umoni_estado', 'A')
                ->whereNotNull('umoni_latitud')
                ->whereNotNull('umoni_longitud')
                ->where('umoni_fechahora', '>=', $desde)
                ->where('umoni_fechahora', '<', $hastaExclusivo)
                ->selectRaw('umoni_user_id, DATE(umoni_fechahora) AS fecha, COUNT(*) AS registros, MIN(umoni_fechahora) AS primera_hora, MAX(umoni_fechahora) AS ultima_hora')
                ->groupBy('umoni_user_id')
                ->groupByRaw('DATE(umoni_fechahora)')
                ->get()
                ->keyBy(fn($registro) => $registro->umoni_user_id . '|' . $registro->fecha);

            // GPS histórico de la misma persona/unidad sin identificación completa.
            // Se informa, pero NO se agrega a los registros de esta operación.
            $gpsHistoricoSinVinculo = UserMonitoreo::query()
                ->whereIn('umoni_user_id', $ids)
                ->where('umoni_uni_cod', $unidadMision)
                ->where(function ($q) {
                    $q->whereNull('umoni_mision_cod')
                        ->orWhereNull('umoni_puesto_cod');
                })
                ->where('umoni_estado', 'A')
                ->whereNotNull('umoni_latitud')
                ->whereNotNull('umoni_longitud')
                ->where('umoni_fechahora', '>=', $desde)
                ->where('umoni_fechahora', '<', $hastaExclusivo)
                ->selectRaw('umoni_user_id, DATE(umoni_fechahora) AS fecha, COUNT(*) AS registros')
                ->groupBy('umoni_user_id')
                ->groupByRaw('DATE(umoni_fechahora)')
                ->get()
                ->keyBy(fn($registro) => $registro->umoni_user_id . '|' . $registro->fecha);

            // La primera fecha en que consta GPS vinculado a alguna operación
            // para este usuario se utiliza como referencia conservadora.
            // Antes de esa fecha, la ausencia de GPS vinculado NO implica falta.
            $primerGpsVinculadoPorUsuario = UserMonitoreo::query()
                ->whereIn('umoni_user_id', $ids)
                ->whereNotNull('umoni_mision_cod')
                ->whereNotNull('umoni_puesto_cod')
                ->whereNotNull('umoni_latitud')
                ->whereNotNull('umoni_longitud')
                ->where('umoni_estado', 'A')
                ->where('umoni_fechahora', '<', $hastaExclusivo)
                ->selectRaw('umoni_user_id, MIN(DATE(umoni_fechahora)) AS primera_fecha')
                ->groupBy('umoni_user_id')
                ->pluck('primera_fecha', 'umoni_user_id');
        }

        $calendario = [];
        foreach ($fechas as $fecha) {
            $calendario[$fecha] = [
                'fecha' => $fecha,
                'personal_con_gps' => 0,
                'personal_sin_gps' => 0,
                'personal_sin_asignacion_verificada' => 0,
                'personal_sin_vinculacion_verificable' => 0,
            ];
        }

        $personal = [];
        foreach ($asignaciones as $userId => $registrosAsignacion) {
            $resumen = [
                'dias_evaluables' => 0,
                'dias_con_gps' => 0,
                'dias_sin_gps' => 0,
                'dias_sin_vinculacion_verificable' => 0,
                'dias_sin_asignacion_verificada' => 0,
            ];
            $dias = [];
            $primeraFechaVinculada = $primerGpsVinculadoPorUsuario->get($userId);
            $primeraFechaVinculada = $primeraFechaVinculada
                ? substr((string) $primeraFechaVinculada, 0, 10)
                : null;

            foreach ($fechas as $fecha) {
                // Una fecha de registro de asignación NO prueba el comienzo
                // efectivo del servicio: se usa como criterio administrativo.
                $periodoSegunRegistro = $registrosAsignacion->contains(
                    function ($asignacion) use ($fecha) {
                        $alta = $asignacion->mitem_fechareg
                            ? substr((string) $asignacion->mitem_fechareg, 0, 10)
                            : null;
                        $baja = $asignacion->mitem_fechabaja
                            ? substr((string) $asignacion->mitem_fechabaja, 0, 10)
                            : null;

                        if ($alta === null || $fecha < $alta) {
                            return false;
                        }

                        if ($baja !== null) {
                            return $fecha <= $baja;
                        }

                        return $asignacion->mitem_estado === 'A';
                    }
                );

                $registro = $gpsPorPersonaYFecha->get($userId . '|' . $fecha);
                $registroHistorico = $gpsHistoricoSinVinculo->get($userId . '|' . $fecha);
                $cantidad = $registro ? (int) $registro->registros : 0;
                $cantidadHistorica = $registroHistorico
                    ? (int) $registroHistorico->registros
                    : 0;

                if ($fecha > $hoy) {
                    $estado = 'futuro';
                } elseif (!$periodoSegunRegistro) {

                    /*
    |--------------------------------------------------------------------------
    | GPS SIN ASIGNACIÓN VERIFICABLE
    |--------------------------------------------------------------------------
    |
    | Puede existir GPS vinculado o histórico, pero sin
    | evidencia suficiente de asignación en esa fecha.
    |
    | No afirmar que el militar incumplió el monitoreo.
    |--------------------------------------------------------------------------
    */

                    $estado = (
                        $cantidad > 0 ||
                        $cantidadHistorica > 0
                    )
                        ? 'gps_sin_asignacion_verificada'
                        : 'sin_asignacion_verificada';

                    $resumen['dias_sin_asignacion_verificada']++;

                    $calendario[$fecha]['personal_sin_asignacion_verificada']++;
                } elseif ($cantidad > 0) {
                    $estado = 'con_gps';
                    $resumen['dias_evaluables']++;
                    $resumen['dias_con_gps']++;
                    $calendario[$fecha]['personal_con_gps']++;
                } elseif (
                    $cantidadHistorica > 0
                    || $primeraFechaVinculada === null
                    || $fecha < $primeraFechaVinculada
                ) {
                    // Registros antiguos sin vínculo o ausencia de evidencia
                    // de que este usuario ya usaba GPS vinculado en esa fecha.
                    $estado = $cantidadHistorica > 0
                        ? 'gps_sin_vinculacion_verificable'
                        : 'sin_vinculacion_verificable';
                    $resumen['dias_sin_vinculacion_verificable']++;
                    $calendario[$fecha]['personal_sin_vinculacion_verificable']++;
                } else {
                    // Solo significa sin GPS EXPLÍCITAMENTE vinculado a
                    // esta operación y puesto; no prueba falta disciplinaria.
                    $estado = 'sin_gps';
                    $resumen['dias_evaluables']++;
                    $resumen['dias_sin_gps']++;
                    $calendario[$fecha]['personal_sin_gps']++;
                }

                $dias[] = [
                    'fecha' => $fecha,
                    'estado' => $estado,
                    'periodo_segun_registro' => $periodoSegunRegistro,
                    'registros_gps' => $cantidad,
                    'registros_sin_vinculacion' => $cantidadHistorica,
                    'primera_hora' => $registro?->primera_hora,
                    'ultima_hora' => $registro?->ultima_hora,
                ];
            }

            $resumen['porcentaje_cobertura'] = $resumen['dias_evaluables'] > 0
                ? round($resumen['dias_con_gps'] * 100 / $resumen['dias_evaluables'], 1)
                : null;

            $primero = $registrosAsignacion->first();
            $nombre = trim((string) ($primero->personal ?? ''));
            $personal[] = [
                'user_id' => (int) $userId,
                'nombre' => $nombre !== '' ? $nombre : 'Usuario ' . $userId,
                'resumen' => $resumen,
                'dias' => $dias,
            ];
        }

        return response()->json([
            'mision_cod' => (int) $mision->mision_cod,
            'mision_descrip' => $mision->mision_descrip,
            'mision_puesto_cod' => (int) $mision->mision_puesto_cod,
            'fecha_inicio' => $inicio->toDateString(),
            'fecha_fin' => $fin->toDateString(),
            'mision_fecha_inicio' => $inicioOriginal->toDateString(),
            'mision_fecha_fin' => $finOriginal->toDateString(),
            'mes_consultado' => $request->input('mes'),
            'fuente' => 'Solo GPS con operación y puesto explícitamente vinculados; GPS histórico sin vínculo se informa por separado.',
            'criterio_asignacion' => 'Período estimado con fecha de registro y baja de asignación; no acredita necesariamente el inicio efectivo del servicio.',
            'criterio_vinculacion' => 'Antes del primer GPS vinculado por usuario, o en fechas con GPS histórico sin vínculo, los días no se califican como incumplimiento.',
            'criterio_cobertura' => 'Con GPS significa al menos una posición vinculada; no implica seguimiento continuo ni presencia dentro del polígono del puesto.',
            'calendario' => array_values($calendario),
            'personal' => $personal,
        ]);
    }


    /**
     * Vista mensual de SOLO historial sin vínculo, cuando el mes solicitado
     * queda totalmente fuera de las fechas oficiales de la operación.
     * La autorización de rol y unidad ya fue validada en controlMonitoreoDiario.
     * No se califica cumplimiento, ausencia ni asignación a la operación.
     */
    private function controlHistoricoFueraDeOperacion(Misione $mision, Carbon $inicio, Carbon $fin)
    {
        $personas = MisioneItem::query()
            ->join('users AS u', 'u.id', '=', 'misione_items.mitem_user_id')
            ->leftJoin('personas AS p', 'p.per_cod', '=', 'u.user_per_cod')
            ->where('misione_items.mitem_mision_cod', $mision->mision_cod)
            ->whereNotNull('misione_items.mitem_user_id')
            ->select('misione_items.mitem_user_id')
            ->selectRaw(self::NOMBRE_MILITAR_SQL . ' AS nombre')
            ->get()->unique('mitem_user_id')->values();
        $ids = $personas->pluck('mitem_user_id')->map(fn($id) => (int) $id)
            ->filter(fn($id) => $id > 0)->values()->all();

        $gpsPorDia = collect();
        if (count($ids)) {
            $gpsPorDia = UserMonitoreo::query()
                ->whereIn('umoni_user_id', $ids)
                ->where('umoni_uni_cod', (int) $mision->mision_uni_cod)
                ->where('umoni_estado', 'A')
                ->whereNotNull('umoni_latitud')
                ->whereNotNull('umoni_longitud')
                ->where(function ($q) {
                    $q->whereNull('umoni_mision_cod')->orWhereNull('umoni_puesto_cod');
                })
                ->where('umoni_fechahora', '>=', $inicio->format('Y-m-d H:i:s'))
                ->where('umoni_fechahora', '<', $fin->copy()->addDay()->format('Y-m-d H:i:s'))
                ->selectRaw('umoni_user_id, DATE(umoni_fechahora) AS fecha, COUNT(*) AS total')
                ->groupBy('umoni_user_id')
                ->groupByRaw('DATE(umoni_fechahora)')
                ->get()->keyBy(fn($r) => $r->umoni_user_id . '|' . $r->fecha);
        }

        $calendario = [];
        for ($dia = $inicio->copy(); $dia->lte($fin); $dia->addDay()) {
            $fecha = $dia->toDateString();
            $calendario[$fecha] = [
                'fecha' => $fecha,
                'personal_con_gps' => 0,
                'personal_sin_gps' => 0,
                'personal_sin_asignacion_verificada' => 0,
                'personal_sin_vinculacion_verificable' => 0,
            ];
        }

        $personal = [];
        foreach ($personas as $persona) {
            $id = (int) $persona->mitem_user_id;
            $dias = [];
            $conHistorico = 0;
            foreach (array_keys($calendario) as $fecha) {
                $gps = $gpsPorDia->get($id . '|' . $fecha);
                $cantidad = (int) ($gps?->total ?? 0);
                if ($cantidad > 0) {
                    $conHistorico++;
                    $calendario[$fecha]['personal_sin_vinculacion_verificable']++;
                }
                $dias[] = [
                    'fecha' => $fecha,
                    'estado' => $cantidad > 0
                        ? 'gps_historico_fuera_periodo'
                        : 'fuera_periodo_operacion',
                    'periodo_segun_registro' => false,
                    'registros_gps' => 0,
                    'registros_sin_vinculacion' => $cantidad,
                    'primera_hora' => null,
                    'ultima_hora' => null,
                ];
            }
            $personal[] = [
                'user_id' => $id,
                'nombre' => $persona->nombre ?: 'Usuario ' . $id,
                'resumen' => [
                    'dias_evaluables' => 0,
                    'dias_con_gps' => 0,
                    'dias_sin_gps' => 0,
                    'dias_sin_vinculacion_verificable' => $conHistorico,
                    'dias_sin_asignacion_verificada' => 0,
                    'porcentaje_cobertura' => null,
                ],
                'dias' => $dias,
            ];
        }

        return response()->json([
            'mision_cod' => (int) $mision->mision_cod,
            'mision_descrip' => $mision->mision_descrip,
            'mision_puesto_cod' => (int) $mision->mision_puesto_cod,
            'fecha_inicio' => $inicio->toDateString(),
            'fecha_fin' => $fin->toDateString(),
            'fuera_periodo_operacion' => true,
            'fuente' => 'Exclusivamente GPS históricos sin vínculo en la unidad, fuera del período oficial de esta operación.',
            'criterio_asignacion' => 'No se evalúa ninguna asignación para este período ajeno a la operación.',
            'criterio_vinculacion' => 'GPS sin códigos de operación o puesto; no acredita ejecución de esta misión.',
            'criterio_cobertura' => 'No calculable fuera de las fechas de la operación.',
            'calendario' => array_values($calendario),
            'personal' => $personal,
        ]);
    }

    public function puestogeojson($puesto_cod)
    {
        $puesto = Puesto::select('puesto_cod', 'puesto_geojson')->where('puesto_cod', $puesto_cod)->first();

        return response()->json([
            "puesto" => $puesto
        ]);
    }

    public function dashboard()
    {
        $user = auth()->user();
        $roleName = $user->getRoleNames()->first();
        $isGlobal = in_array($roleName, ['Administrador', 'Super-Admin', 'Admin-Dpto-III'], true);

        if ($isGlobal) {
            $unidadesIds = null;
        } elseif ($roleName === 'G-3') {
            $unidadesIds = Unidade::where('uni_estado', 'A')
                ->where('uni_codsup', $user->user_uni_cod)
                ->pluck('uni_cod');
        } elseif (in_array($roleName, ['P-3', 'Personal'], true)) {
            $unidadesIds = collect([$user->user_uni_cod])->filter();
        } else {
            return response()->json([
                'message' => 'El rol no tiene acceso al panel operativo.',
            ], 403);
        }

        $unidadesQuery = Unidade::where('uni_estado', 'A');
        $misionesQuery = Misione::where('mision_estado', 'A');
        $personalQuery = Persona::where('personas.per_estado', 'A');

        if ($unidadesIds !== null) {
            $unidadesQuery->whereIn('uni_cod', $unidadesIds);
            $misionesQuery->whereIn('mision_uni_cod', $unidadesIds);
            $personalQuery
                ->join('destino_persona_unidades as dpu', 'dpu.dpuni_per_cod', '=', 'personas.per_cod')
                ->where('dpu.dpuni_estado', 'A')
                ->whereIn('dpu.dpuni_uni_cod', $unidadesIds);
        }

        $startOfDay = now()->startOfDay();
        $endOfDay = $startOfDay->copy()->addDay();
        $positionsQuery = UserMonitoreo::query()
            ->join('users', 'users.id', '=', 'user_monitoreos.umoni_user_id')
            ->leftJoin('personas', 'personas.per_cod', '=', 'users.user_per_cod')
            ->leftJoin('persona_grados as pg', function ($join) {
                $join->on('pg.pergrad_per_cod', '=', 'personas.per_cod')
                    ->where('pg.pergrad_estado', '=', 'A');
            })
            ->leftJoin('grados', 'grados.grad_cod', '=', 'pg.pergrad_grad_cod')
            ->leftJoin('persona_especialidades as pe', function ($join) {
                $join->on('pe.perespe_per_cod', '=', 'personas.per_cod')
                    ->where('pe.perespe_estado', '=', 'A');
            })
            ->leftJoin('especialidades', 'especialidades.espe_cod', '=', 'pe.perespe_espe_cod')
            ->leftJoin('unidades', 'unidades.uni_cod', '=', 'user_monitoreos.umoni_uni_cod')
            ->where('user_monitoreos.umoni_estado', 'A')
            ->where('user_monitoreos.umoni_fechahora', '>=', $startOfDay)
            ->where('user_monitoreos.umoni_fechahora', '<', $endOfDay);

        if ($unidadesIds !== null) {
            $positionsQuery->whereIn('user_monitoreos.umoni_uni_cod', $unidadesIds);
        }

        $posicionesreportadas = (clone $positionsQuery)->count('user_monitoreos.umoni_cod');
        $lastPositions = $positionsQuery
            ->select(
                'user_monitoreos.umoni_cod',
                'user_monitoreos.umoni_user_id',
                'user_monitoreos.umoni_uni_cod',
                'user_monitoreos.umoni_latitud',
                'user_monitoreos.umoni_longitud',
                'user_monitoreos.umoni_fechahora',
                'unidades.uni_abrev',
                DB::raw("TRIM(CONCAT(COALESCE(grados.grad_abrev, ''), ' ', COALESCE(especialidades.espe_abrev, ''), ' ', COALESCE(personas.per_nombres, ''), ' ', COALESCE(personas.per_apaterno, ''), ' ', COALESCE(personas.per_amaterno, ''))) AS personal")
            )
            ->orderByDesc('user_monitoreos.umoni_fechahora')
            ->limit(50)
            ->get();

        return response()->json([
            'uniactivas' => $unidadesQuery->count(),
            'personalactivo' => $personalQuery->distinct('personas.per_cod')->count('personas.per_cod'),
            'misionesactivas' => $misionesQuery->count(),
            'posicionesreportadas' => $posicionesreportadas,
            'last_positions' => $lastPositions,
        ]);
    }
}
