<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Api\ApiController;
use App\Models\AppDispositivo;
use App\Models\AppSyncQueue;
use App\Models\MantProgramacion;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use OpenApi\Attributes as OA;

#[OA\Schema(
    schema: "OperacionSync",
    required: ["tabla", "operacion", "local_id", "data", "fecha_creacion_local"],
    properties: [
        new OA\Property(property: "tabla", type: "string", description: "Tabla destino: mant_estado_historial, mant_checklist_respuestas, gps_tracking"),
        new OA\Property(property: "operacion", type: "string", enum: ["CREATE", "UPDATE", "DELETE"]),
        new OA\Property(property: "local_id", type: "string", description: "ID único local para tracking"),
        new OA\Property(property: "data", type: "object", description: "Datos de la operación"),
        new OA\Property(property: "fecha_creacion_local", type: "string", format: "date-time"),
        new OA\Property(property: "prioridad", type: "integer", minimum: 1, maximum: 10, default: 5)
    ]
)]
#[OA\Schema(
    schema: "SyncUploadRequest",
    required: ["dispositivo_id", "operaciones"],
    properties: [
        new OA\Property(property: "dispositivo_id", type: "integer"),
        new OA\Property(
            property: "operaciones",
            type: "array",
            items: new OA\Items(ref: "#/components/schemas/OperacionSync")
        )
    ]
)]
#[OA\Schema(
    schema: "RegistrarDispositivoRequest",
    required: ["device_id", "plataforma"],
    properties: [
        new OA\Property(property: "device_id", type: "string", maxLength: 255, description: "ID único del dispositivo"),
        new OA\Property(property: "fcm_token", type: "string", maxLength: 500, description: "Token Firebase Cloud Messaging"),
        new OA\Property(property: "plataforma", type: "string", enum: ["ANDROID", "IOS"]),
        new OA\Property(property: "modelo", type: "string", maxLength: 100),
        new OA\Property(property: "version_os", type: "string", maxLength: 50),
        new OA\Property(property: "version_app", type: "string", maxLength: 20)
    ]
)]
class SyncController extends ApiController
{
    /**
     * Obtiene el usu_id del usuario autenticado para filtrar mantenimientos
     * Compatible con modelo User (tabla users) que tiene campo usu_id vinculado
     */
    private function getTecnicoId(): ?int
    {
        $user = $this->usuarioActual();
        if (!$user) {
            return null;
        }

        // Si es modelo User (tabla users), usar el campo usu_id que vincula con usuarios
        if ($user instanceof \App\Models\User) {
            return $user->usu_id;
        }

        // Si es modelo Usuario (tabla usuarios), usar usu_id directamente
        if ($user instanceof \App\Models\Usuario) {
            return $user->usu_id;
        }

        return null;
    }

    /**
     * Descargar catálogos para APP offline
     * GET /api/v1/sync/catalogos
     */
    #[OA\Get(
        path: "/api/v1/sync/catalogos",
        summary: "Descargar catálogos para APP offline",
        description: "Descarga todos los catálogos necesarios para trabajar offline: estados, equipos, items de revisión, peajes, vías, inventario y mantenimientos asignados (próximos 7 días)",
        tags: ["Sincronización"],
        security: [["sanctum" => []]],
        parameters: [
            new OA\Parameter(name: "desde", in: "query", required: false, description: "Para sincronización incremental", schema: new OA\Schema(type: "string", format: "date"))
        ],
        responses: [
            new OA\Response(
                response: 200,
                description: "Catálogos descargados",
                content: new OA\JsonContent(properties: [
                    new OA\Property(property: "success", type: "boolean", example: true),
                    new OA\Property(property: "data", type: "object", properties: [
                        new OA\Property(property: "timestamp", type: "string", format: "date-time"),
                        new OA\Property(property: "usuario", type: "object"),
                        new OA\Property(property: "catalogos", type: "object", properties: [
                            new OA\Property(property: "estados", type: "array", items: new OA\Items(type: "object")),
                            new OA\Property(property: "estados_equipos", type: "array", items: new OA\Items(type: "object")),
                            new OA\Property(property: "items_revision", type: "array", items: new OA\Items(type: "object")),
                            new OA\Property(property: "hardware_tipos", type: "array", items: new OA\Items(type: "object")),
                            new OA\Property(property: "hardware_marcas", type: "array", items: new OA\Items(type: "object"))
                        ]),
                        new OA\Property(property: "estructura", type: "object", properties: [
                            new OA\Property(property: "peajes", type: "array", items: new OA\Items(type: "object")),
                            new OA\Property(property: "vias", type: "array", items: new OA\Items(type: "object"))
                        ]),
                        new OA\Property(property: "mantenimientos", type: "array", items: new OA\Items(type: "object")),
                        new OA\Property(property: "inventario", type: "array", items: new OA\Items(type: "object"))
                    ])
                ])
            )
        ]
    )]
    public function catalogos(Request $request): JsonResponse
    {
        return $this->ejecutar(function () use ($request) {

            $request->validate([
                'desde' => 'nullable|date', // Para sincronización incremental
            ]);

            $usuario = $this->usuarioActual();

            // Estados de mantenimiento
            $estados = DB::table('mant_estados')
                ->where('estado', 1)
                ->select(['estado_id', 'estado_codigo', 'estado_nombre', 'color_hex', 'orden', 'requiere_gps'])
                ->orderBy('orden')
                ->get();

            // Estados de equipos
            $estadosEquipos = DB::table('mant_estado_equipos')
                ->where('estado', 1)
                ->select(['estado_equipo_id', 'codigo', 'nombre', 'color_hex', 'icono'])
                ->get();

            // Items de revisión
            $itemsRevision = DB::table('items_revision_mantenimiento')
                ->where('estado', 1)
                ->select(['id', 'nombre', 'descripcion', 'orden', 'obligatorio'])
                ->orderBy('orden')
                ->get();

            // Peajes (solo los que le corresponden al técnico por concesión)
            $peajes = DB::table('peaje_centro as pc')
                ->join('concesiones as c', 'pc.id_concesion', '=', 'c.id_concesion')
                ->where('pc.estado', 1)
                ->where('pc.mantenimiento', 1)
                ->select([
                    'pc.id_centro',
                    'pc.nombre_centro',
                    'pc.latitud',
                    'pc.longitud',
                    'c.id_concesion',
                    'c.nombre as concesion',
                ])
                ->get();

            // Vías por peaje
            $vias = DB::table('via_subcentro')
                ->where('estado', 1)
                ->select(['via_id', 'id_centro', 'nombre'])
                ->get();

            // Hardware maestro (tipos)
            $hardwareTipos = DB::table('hardware_maestro')
                ->where('estado', 1)
                ->select(['hardware_id', 'nombre'])
                ->get();

            // Hardware marcas
            $hardwareMarcas = DB::table('hardware_marca')
                ->where('estado', 1)
                ->select(['marca_id', 'nombre'])
                ->get();

            // Mantenimientos asignados al técnico (próximos 7 días)
            $tecnicoId = $this->getTecnicoId();
            $mantenimientosAsignados = MantProgramacion::with([
                'estadoMantenimiento:estado_id,estado_codigo,estado_nombre,color_hex,requiere_gps',
                'centro:id_centro,nombre_centro,latitud,longitud,id_concesion',
                'centro.concesion:id_concesion,nombre',
                'via:via_id,nombre',
            ])
                ->where('tecnico_asignado', $tecnicoId)
                ->where('fecha_programada', '>=', now()->toDateString())
                ->where('fecha_programada', '<=', now()->addDays(7)->toDateString())
                ->activos()
                ->get()
                ->map(fn($m) => [
                    'prog_id' => $m->prog_id,
                    'fecha_programada' => $m->fecha_programada,
                    'observaciones' => $m->observaciones,
                    'estado_id' => $m->estado_id,
                    'estado_codigo' => $m->estadoMantenimiento->estado_codigo,
                    'estado_nombre' => $m->estadoMantenimiento->estado_nombre,
                    'estado_color' => $m->estadoMantenimiento->color_hex,
                    'requiere_gps' => $m->estadoMantenimiento->requiere_gps,
                    'qr_code' => $m->qr_code,
                    'qr_expira' => $m->qr_expira,
                    'id_centro' => $m->id_centro,
                    'peaje_nombre' => $m->centro->nombre_centro,
                    'peaje_lat' => $m->centro->latitud,
                    'peaje_lng' => $m->centro->longitud,
                    'concesion' => $m->centro->concesion->nombre,
                    'via_id' => $m->via_id,
                    'via_nombre' => $m->via?->nombre,
                ]);

            // Inventario por peaje asignado
            $peajesIds = $mantenimientosAsignados->pluck('id_centro')->unique();
            $inventario = DB::table('hardware_inventario as hi')
                ->join('hardware_maestro as hm', 'hi.maestro_id', '=', 'hm.hardware_id')
                ->leftJoin('hardware_marca as hma', 'hi.marca_id', '=', 'hma.marca_id')
                ->join('via_subcentro as vs', 'hi.via_id', '=', 'vs.via_id')
                ->whereIn('vs.id_centro', $peajesIds)
                ->where('hi.estado', 1)
                ->select([
                    'hi.inventario_id',
                    'hi.via_id',
                    'vs.id_centro',
                    'hm.hardware_id as maestro_id',
                    'hm.nombre as tipo',
                    'hma.marca_id',
                    'hma.nombre as marca',
                    'hi.modelo',
                    'hi.codigo1 as serie',
                ])
                ->get();

            return $this->successResponse([
                'timestamp' => now()->toIso8601String(),
                'usuario' => [
                    'id' => $usuario->usu_id,
                    'nombre' => $usuario->nombre_completo,
                ],
                'catalogos' => [
                    'estados' => $estados,
                    'estados_equipos' => $estadosEquipos,
                    'items_revision' => $itemsRevision,
                    'hardware_tipos' => $hardwareTipos,
                    'hardware_marcas' => $hardwareMarcas,
                ],
                'estructura' => [
                    'peajes' => $peajes,
                    'vias' => $vias,
                ],
                'mantenimientos' => $mantenimientosAsignados,
                'inventario' => $inventario,
            ]);

        }, 'Error descargando catálogos');
    }

    /**
     * Subir datos offline
     * POST /api/v1/sync/upload
     */
    #[OA\Post(
        path: "/api/v1/sync/upload",
        summary: "Subir datos offline",
        description: "Sincroniza operaciones realizadas offline. Procesa cambios de estado, checklists y tracking GPS",
        tags: ["Sincronización"],
        security: [["sanctum" => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(ref: "#/components/schemas/SyncUploadRequest")
        ),
        responses: [
            new OA\Response(
                response: 200,
                description: "Sincronización completada",
                content: new OA\JsonContent(properties: [
                    new OA\Property(property: "success", type: "boolean", example: true),
                    new OA\Property(property: "message", type: "string"),
                    new OA\Property(property: "data", type: "object", properties: [
                        new OA\Property(property: "recibidas", type: "integer"),
                        new OA\Property(property: "procesadas", type: "integer"),
                        new OA\Property(property: "errores", type: "array", items: new OA\Items(
                            properties: [
                                new OA\Property(property: "index", type: "integer"),
                                new OA\Property(property: "local_id", type: "string"),
                                new OA\Property(property: "error", type: "string")
                            ]
                        ))
                    ])
                ])
            ),
            new OA\Response(response: 422, description: "Error de validación")
        ]
    )]
    public function upload(Request $request): JsonResponse
    {
        return $this->ejecutar(function () use ($request) {

            $request->validate([
                'dispositivo_id' => 'required|integer',
                'operaciones' => 'required|array|min:1',
                'operaciones.*.tabla' => 'required|string',
                'operaciones.*.operacion' => 'required|string|in:CREATE,UPDATE,DELETE',
                'operaciones.*.local_id' => 'required|string',
                'operaciones.*.data' => 'required|array',
                'operaciones.*.fecha_creacion_local' => 'required|date',
                'operaciones.*.prioridad' => 'nullable|integer|min:1|max:10',
            ]);

            $usuario = $this->usuarioActual();
            $procesados = 0;
            $errores = [];

            DB::beginTransaction();
            try {
                foreach ($request->operaciones as $index => $op) {
                    try {
                        // Crear registro en cola de sincronización
                        $syncItem = AppSyncQueue::create([
                            'usuario_id' => $usuario->usu_id,
                            'dispositivo_id' => $request->dispositivo_id,
                            'tabla_destino' => $op['tabla'],
                            'operacion' => $op['operacion'],
                            'data_json' => $op['data'],
                            'local_id' => $op['local_id'],
                            'prioridad' => $op['prioridad'] ?? AppSyncQueue::PRIORIDAD_MEDIA,
                            'fecha_creacion_local' => $op['fecha_creacion_local'],
                            'estado' => AppSyncQueue::ESTADO_PENDIENTE,
                        ]);

                        // Procesar inmediatamente según la tabla
                        $resultado = $this->procesarOperacion($syncItem);

                        if ($resultado['success']) {
                            $syncItem->marcarProcesado($resultado['server_id'] ?? null);
                            $procesados++;
                        } else {
                            $syncItem->marcarError($resultado['error']);
                            $errores[] = [
                                'index' => $index,
                                'local_id' => $op['local_id'],
                                'error' => $resultado['error'],
                            ];
                        }

                    } catch (\Exception $e) {
                        $errores[] = [
                            'index' => $index,
                            'local_id' => $op['local_id'],
                            'error' => 'Error procesando operación',
                        ];
                    }
                }

                DB::commit();

                return $this->successResponse([
                    'recibidas' => count($request->operaciones),
                    'procesadas' => $procesados,
                    'errores' => $errores,
                ], "Sincronización completada: {$procesados} operaciones procesadas");

            } catch (\Exception $e) {
                DB::rollBack();
                throw $e;
            }

        }, 'Error sincronizando datos');
    }

    /**
     * Verificar operaciones pendientes
     * GET /api/v1/sync/pendientes
     */
    #[OA\Get(
        path: "/api/v1/sync/pendientes",
        summary: "Verificar operaciones pendientes",
        description: "Verifica si hay operaciones de sincronización pendientes o con error para el usuario",
        tags: ["Sincronización"],
        security: [["sanctum" => []]],
        responses: [
            new OA\Response(
                response: 200,
                description: "Estado de sincronización",
                content: new OA\JsonContent(properties: [
                    new OA\Property(property: "success", type: "boolean", example: true),
                    new OA\Property(property: "data", type: "object", properties: [
                        new OA\Property(property: "pendientes", type: "integer"),
                        new OA\Property(property: "con_error", type: "integer"),
                        new OA\Property(property: "total", type: "integer")
                    ])
                ])
            )
        ]
    )]
    public function pendientes(): JsonResponse
    {
        return $this->ejecutar(function () {

            $usuario = $this->usuarioActual();

            $pendientes = AppSyncQueue::where('usuario_id', $usuario->usu_id)
                ->where('estado', AppSyncQueue::ESTADO_PENDIENTE)
                ->count();

            $errores = AppSyncQueue::where('usuario_id', $usuario->usu_id)
                ->where('estado', AppSyncQueue::ESTADO_ERROR)
                ->where('intentos', '<', 5)
                ->count();

            return $this->successResponse([
                'pendientes' => $pendientes,
                'con_error' => $errores,
                'total' => $pendientes + $errores,
            ]);

        }, 'Error verificando pendientes');
    }

    /**
     * Registrar dispositivo
     * POST /api/v1/sync/dispositivo
     */
    #[OA\Post(
        path: "/api/v1/sync/dispositivo",
        summary: "Registrar dispositivo móvil",
        description: "Registra o actualiza un dispositivo móvil para notificaciones push. Desactiva otros dispositivos del usuario",
        tags: ["Sincronización"],
        security: [["sanctum" => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(ref: "#/components/schemas/RegistrarDispositivoRequest")
        ),
        responses: [
            new OA\Response(
                response: 200,
                description: "Dispositivo registrado exitosamente",
                content: new OA\JsonContent(properties: [
                    new OA\Property(property: "success", type: "boolean", example: true),
                    new OA\Property(property: "message", type: "string"),
                    new OA\Property(property: "data", type: "object", properties: [
                        new OA\Property(property: "dispositivo_id", type: "integer")
                    ])
                ])
            )
        ]
    )]
    public function registrarDispositivo(Request $request): JsonResponse
    {
        return $this->ejecutar(function () use ($request) {

            $request->validate([
                'device_id' => 'required|string|max:255',
                'fcm_token' => 'nullable|string|max:500',
                'plataforma' => 'required|string|in:ANDROID,IOS',
                'modelo' => 'nullable|string|max:100',
                'version_os' => 'nullable|string|max:50',
                'version_app' => 'nullable|string|max:20',
            ]);

            $usuario = $this->usuarioActual();

            // Buscar dispositivo existente
            $dispositivo = AppDispositivo::where('usuario_id', $usuario->usu_id)
                ->where('device_id', $request->device_id)
                ->first();

            if ($dispositivo) {
                // Actualizar
                $dispositivo->update([
                    'fcm_token' => $request->fcm_token,
                    'modelo' => $request->modelo,
                    'version_os' => $request->version_os,
                    'version_app' => $request->version_app,
                    'activo' => true,
                    'ultimo_acceso' => now(),
                    'fecha_actualizacion' => now(),
                ]);
            } else {
                // Crear nuevo
                $dispositivo = AppDispositivo::create([
                    'usuario_id' => $usuario->usu_id,
                    'device_id' => $request->device_id,
                    'fcm_token' => $request->fcm_token,
                    'plataforma' => $request->plataforma,
                    'modelo' => $request->modelo,
                    'version_os' => $request->version_os,
                    'version_app' => $request->version_app,
                    'activo' => true,
                    'ultimo_acceso' => now(),
                ]);
            }

            // Desactivar otros dispositivos del mismo usuario
            AppDispositivo::where('usuario_id', $usuario->usu_id)
                ->where('dispositivo_id', '!=', $dispositivo->dispositivo_id)
                ->update(['activo' => false]);

            return $this->successResponse([
                'dispositivo_id' => $dispositivo->dispositivo_id,
            ], 'Dispositivo registrado exitosamente');

        }, 'Error registrando dispositivo');
    }

    /**
     * Actualizar token FCM
     * PUT /api/v1/sync/dispositivo/token
     */
    #[OA\Put(
        path: "/api/v1/sync/dispositivo/token",
        summary: "Actualizar token FCM",
        description: "Actualiza el token Firebase Cloud Messaging de un dispositivo registrado",
        tags: ["Sincronización"],
        security: [["sanctum" => []]],
        requestBody: new OA\RequestBody(
            required: true,
            content: new OA\JsonContent(
                required: ["device_id", "fcm_token"],
                properties: [
                    new OA\Property(property: "device_id", type: "string"),
                    new OA\Property(property: "fcm_token", type: "string", maxLength: 500)
                ]
            )
        ),
        responses: [
            new OA\Response(
                response: 200,
                description: "Token actualizado",
                content: new OA\JsonContent(properties: [
                    new OA\Property(property: "success", type: "boolean", example: true),
                    new OA\Property(property: "message", type: "string"),
                    new OA\Property(property: "data", type: "object", properties: [
                        new OA\Property(property: "dispositivo_id", type: "integer")
                    ])
                ])
            ),
            new OA\Response(response: 404, description: "Dispositivo no encontrado")
        ]
    )]
    public function actualizarToken(Request $request): JsonResponse
    {
        return $this->ejecutar(function () use ($request) {

            $request->validate([
                'device_id' => 'required|string',
                'fcm_token' => 'required|string|max:500',
            ]);

            $usuario = $this->usuarioActual();

            $dispositivo = AppDispositivo::where('usuario_id', $usuario->usu_id)
                ->where('device_id', $request->device_id)
                ->first();

            if (!$dispositivo) {
                return $this->notFoundResponse('Dispositivo no encontrado');
            }

            $dispositivo->actualizarToken($request->fcm_token);

            return $this->successResponse([
                'dispositivo_id' => $dispositivo->dispositivo_id,
            ], 'Token actualizado');

        }, 'Error actualizando token');
    }

    /**
     * Procesar operación de sincronización
     */
    private function procesarOperacion(AppSyncQueue $syncItem): array
    {
        $tabla = $syncItem->tabla_destino;
        $operacion = $syncItem->operacion;
        $data = $syncItem->data_json;

        try {
            switch ($tabla) {
                case 'mant_estado_historial':
                    return $this->procesarEstadoHistorial($operacion, $data);

                case 'mant_checklist_respuestas':
                    return $this->procesarChecklistRespuesta($operacion, $data);

                case 'gps_tracking':
                    return $this->procesarGpsTracking($operacion, $data);

                default:
                    return ['success' => false, 'error' => 'Tabla no soportada para sincronización'];
            }
        } catch (\Exception $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    private function procesarEstadoHistorial(string $operacion, array $data): array
    {
        if ($operacion === 'CREATE') {
            // Llamar al SP de cambio de estado
            $resultado = $this->ejecutarSP('sp_cambiar_estado_mant', [
                $data['prog_id'],
                $data['estado_codigo'],
                $data['latitud'] ?? null,
                $data['longitud'] ?? null,
                $data['precision'] ?? null,
                $data['observaciones'] ?? null,
                $data['usuario_id'],
            ]);

            $respuesta = $this->parsearRespuestaSP($resultado);
            return [
                'success' => $respuesta['success'] ?? false,
                'error' => $respuesta['message'] ?? null,
            ];
        }

        return ['success' => false, 'error' => 'Operación no soportada'];
    }

    private function procesarChecklistRespuesta(string $operacion, array $data): array
    {
        if ($operacion === 'CREATE' || $operacion === 'UPDATE') {
            $resultado = $this->ejecutarSP('sp_guardar_checklist', [
                $data['prog_id'],
                $data['inventario_id'],
                $data['via_id'],           // ← AGREGADO: parámetro faltante
                $data['id_item'],
                $data['estado_equipo_id'],
                $data['valor_medicion'] ?? null,
                $data['observacion'] ?? null,
                $data['usuario_id'],
            ]);

            $respuesta = $this->parsearRespuestaSP($resultado);
            return [
                'success' => $respuesta['success'] ?? false,
                'error' => $respuesta['message'] ?? null,
            ];
        }

        return ['success' => false, 'error' => 'Operación no soportada'];
    }

    private function procesarGpsTracking(string $operacion, array $data): array
    {
        if ($operacion === 'CREATE') {
            $resultado = $this->ejecutarSP('sp_registrar_posicion_gps', [
                $data['usuario_id'],
                $data['prog_id'] ?? null,
                $data['latitud'],
                $data['longitud'],
                $data['precision'] ?? null,
                $data['altitud'] ?? null,
                $data['velocidad'] ?? null,
                $data['bateria'] ?? null,
                $data['fecha_captura'],
            ]);

            $respuesta = $this->parsearRespuestaSP($resultado);
            return [
                'success' => $respuesta['success'] ?? false,
                'server_id' => $respuesta['data']['id'] ?? null,
                'error' => $respuesta['message'] ?? null,
            ];
        }

        return ['success' => false, 'error' => 'Operación no soportada'];
    }
}
