<?php
/**
 * POST /api/apply.php
 *
 * Aplica una operación individual (insert, update, delete, bulk_insert)
 * a una tabla específica en MySQL.
 *
 * Request:
 *   {
 *     "table": "Docentes",
 *     "type": "insert",
 *     "record": { "id": "123", "nombre": "Juan", ... }
 *   }
 *
 * Response:
 *   { "ok": true, "guardado": "2026-08-25 14:30:45", "version": "abc123..." }
 */

require __DIR__ . '/_bootstrap.php';

requireAuth();

$body = getJsonBody();
$turnoId = getTurnoId();
$pdo = getPDO();

// ===== VALIDAR TABLA =====
$tablasValidas = [
    'Docentes', 'Grupos', 'Asignaturas', 'PlanSintetico', 'ProgAnalitico',
    'Proyectos', 'PMCE', 'PAT', 'DatosEscuela', 'Problematicas', 'Eventos'
];

$tabla = $body['table'] ?? null;
$tipo = $body['type'] ?? null;

if (!in_array($tabla, $tablasValidas, true)) {
    errorJSON('Tabla no reconocida: ' . $tabla, 400);
}

if (!in_array($tipo, ['insert', 'update', 'delete', 'bulk_insert', 'update_escuela'], true)) {
    errorJSON('Tipo de operación no reconocido: ' . $tipo, 400);
}

// ===== MAPEAR TABLA A TABLA SQL =====
$tablaSQL = [
    'Docentes' => 'docentes',
    'Grupos' => 'grupos',
    'Asignaturas' => 'asignaturas',
    'PlanSintetico' => 'plan_sintetico',
    'ProgAnalitico' => 'programa_analitico',
    'Proyectos' => 'proyectos',
    'PMCE' => 'pmce',
    'PAT' => 'pat',
    'DatosEscuela' => 'datos_escuela',
    'Problematicas' => 'problematicas',
    'Eventos' => 'eventos',
][$tabla];

// ===== EJECUTAR OPERACIÓN =====
try {
    $pdo->beginTransaction();

    switch ($tipo) {
        case 'insert':
            aplicarInsert($pdo, $tabla, $tablaSQL, $body, $turnoId);
            break;

        case 'bulk_insert':
            aplicarBulkInsert($pdo, $tabla, $tablaSQL, $body, $turnoId);
            break;

        case 'update':
            aplicarUpdate($pdo, $tabla, $tablaSQL, $body, $turnoId);
            break;

        case 'delete':
            aplicarDelete($pdo, $tabla, $tablaSQL, $body, $turnoId);
            break;

        case 'update_escuela':
            aplicarUpdateEscuela($pdo, $tablaSQL, $body, $turnoId);
            break;
    }

    $pdo->commit();

    // Registrar en versiones
    registrarVersion($turnoId, $tabla, $tipo);

    // Calculamos y devolvemos la versión nueva para que el propio navegador que
    // acaba de guardar actualice su "remoteVersion". Si no se manda esto, el
    // polling de checkForRemoteChanges() detecta el cambio que TÚ MISMO acabas
    // de hacer como si fuera de otra persona, 8 segundos después.
    exitJSON([
        'ok' => true,
        'guardado' => date('Y-m-d H:i:s'),
        'version' => calcularVersionActual($pdo, $turnoId),
    ]);

} catch (Exception $e) {
    $pdo->rollBack();
    errorJSON('Error al guardar: ' . $e->getMessage(), 500);
}

// ===== FUNCIONES AUXILIARES =====

function aplicarInsert($pdo, $tabla, $tablaSQL, $body, $turnoId) {
    $registro = $body['record'] ?? null;
    if (!$registro || !isset($registro['id'])) {
        throw new Exception('Insert requiere un registro con "id"');
    }

    // Para la mayoría de tablas, verificar si el ID ya existe (upsert)
    $stmt = $pdo->prepare("SELECT id FROM $tablaSQL WHERE id = ? AND turno_id = ?");
    $stmt->execute([$registro['id'], $turnoId]);
    $existe = $stmt->fetch();

    if ($existe && in_array($tabla, ['Problematicas', 'Eventos'])) {
        // Estas tablas usan ID string, así que hacer upsert (update si existe)
        aplicarUpdate($pdo, $tabla, $tablaSQL, ['id' => $registro['id'], 'updatedRecord' => $registro], $turnoId);
        return;
    }

    // Construir INSERT
    $campos = array_keys($registro);
    campos_validos($tabla, $campos); // Validar que los campos son permitidos

    foreach ($registro as $campo => $valor) {
        $registro[$campo] = normalizarValor($campo, $valor);
    }

    $campos[] = 'turno_id';
    $valores = array_values($registro);
    $valores[] = $turnoId;

    $placeholders = implode(',', array_fill(0, count($campos), '?'));
    $camposStr = implode(',', $campos);

    $sql = "INSERT INTO $tablaSQL ($camposStr) VALUES ($placeholders)";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($valores);
}

function aplicarBulkInsert($pdo, $tabla, $tablaSQL, $body, $turnoId) {
    $registros = $body['records'] ?? [];
    if (empty($registros)) {
        return;
    }

    // Primero, eliminar registros con los IDs que vamos a insertar
    $ids = array_column($registros, 'id');
    $placeholders = implode(',', array_fill(0, count($ids), '?'));
    $pdo->prepare("DELETE FROM $tablaSQL WHERE turno_id = ? AND id IN ($placeholders)")
        ->execute(array_merge([$turnoId], $ids));

    // Ahora insertar los nuevos
    foreach ($registros as $registro) {
        aplicarInsert($pdo, $tabla, $tablaSQL, ['record' => $registro], $turnoId);
    }
}

function aplicarUpdate($pdo, $tabla, $tablaSQL, $body, $turnoId) {
    $id = $body['id'] ?? null;
    $cambios = $body['updatedRecord'] ?? [];

    if (!$id) {
        throw new Exception('Update requiere "id"');
    }

    if (empty($cambios)) {
        return;
    }

    campos_validos($tabla, array_keys($cambios));

    $setClauses = [];
    $valores = [];
    foreach ($cambios as $campo => $valor) {
        $setClauses[] = "$campo = ?";
        $valores[] = normalizarValor($campo, $valor);
    }

    $valores[] = $turnoId;
    $valores[] = $id;

    $sql = "UPDATE $tablaSQL SET " . implode(', ', $setClauses) . " WHERE turno_id = ? AND id = ?";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($valores);
}

function aplicarDelete($pdo, $tabla, $tablaSQL, $body, $turnoId) {
    $id = $body['id'] ?? null;

    if (!$id) {
        throw new Exception('Delete requiere "id"');
    }

    $stmt = $pdo->prepare("DELETE FROM $tablaSQL WHERE turno_id = ? AND id = ?");
    $stmt->execute([$turnoId, $id]);
}

/**
 * DatosEscuela es un caso especial: es UN SOLO registro por turno (el
 * perfil de la escuela), y a diferencia de las demás tablas el cliente
 * nunca le asigna un "id" (JsonDB.insert() no se usa para esta tabla,
 * el frontend simplemente reemplaza DB.data.DatosEscuela[0]). Por eso no
 * encaja en aplicarInsert()/aplicarUpdate(), que requieren "id": aquí
 * hacemos upsert por turno_id — actualiza si ya existe la fila del turno,
 * o la crea si es la primera vez que se guarda.
 */
function aplicarUpdateEscuela($pdo, $tablaSQL, $body, $turnoId) {
    $registro = $body['record'] ?? null;
    if (!is_array($registro)) {
        throw new Exception('update_escuela requiere "record"');
    }

    campos_validos('DatosEscuela', array_keys($registro));

    foreach ($registro as $campo => $valor) {
        $registro[$campo] = normalizarValor($campo, $valor);
    }

    $stmt = $pdo->prepare("SELECT id FROM $tablaSQL WHERE turno_id = ? LIMIT 1");
    $stmt->execute([$turnoId]);
    $existente = $stmt->fetch();

    if ($existente) {
        $setClauses = [];
        $valores = [];
        foreach ($registro as $campo => $valor) {
            $setClauses[] = "$campo = ?";
            $valores[] = $valor;
        }
        $valores[] = $existente['id'];

        $sql = "UPDATE $tablaSQL SET " . implode(', ', $setClauses) . " WHERE id = ?";
        $pdo->prepare($sql)->execute($valores);
    } else {
        $campos = array_keys($registro);
        $campos[] = 'turno_id';
        $valores = array_values($registro);
        $valores[] = $turnoId;

        $placeholders = implode(',', array_fill(0, count($campos), '?'));
        $camposStr = implode(',', $campos);

        $pdo->prepare("INSERT INTO $tablaSQL ($camposStr) VALUES ($placeholders)")->execute($valores);
    }
}

/**
 * Validar que los campos que se intenta modificar son permitidos
 * (esto evita que alguien intente inyectar campos como turno_id o timestamps)
 */
function campos_validos($tabla, $campos) {
    $permitidos = [
        'Docentes' => ['nombre', 'email', 'especialidad', 'id'],
        'Grupos' => ['nombre', 'grado', 'seccion', 'id'],
        'Asignaturas' => ['nombre', 'campo', 'id'],
        'PlanSintetico' => ['campo', 'asignatura', 'grado', 'contenido', 'tema', 'pda', 'id'],
        'ProgAnalitico' => [
            'id', 'asignatura', 'grado', 'contenido', 'periodo', 'docente', 'campo',
            'problematica', 'temas', 'contexto', 'ejes', 'pmce_vinculo', 'pat_dimension',
            'accion_mejoras', 'eval_diagnostica', 'eval_formativa', 'eval_autoeval',
            'eval_coeval', 'eval_heteroeval', 'instrumentos', 'evidencias_eval', 'criterios',
            'fecha_inicio', 'fecha_fin', 'fecha_real', 'avance', 'prioridad', 'seguimiento_avances',
        ],
        'Proyectos' => [
            'id', 'nombre', 'periodo', 'problematica', 'metodologia', 'fechas',
            'justificacion', 'objetivo', 'docentes', 'avance', 'avance_observaciones',
        ],
        'PMCE' => ['id', 'ambito', 'diagnostico', 'objetivo', 'meta', 'cumplimiento'],
        'PAT' => ['id', 'pmce_id', 'actividad', 'fecha_inicio', 'fecha_fin', 'responsable', 'recursos', 'avance'],
        'DatosEscuela' => [
            'nombre', 'turno', 'cct', 'zona', 'sector', 'municipio', 'estado', 'ciclo', 'fase', 'grados',
            'contexto_social', 'contexto_economico', 'contexto_cultural', 'contexto_ambiental',
            'contexto_linguistico', 'caracteristicas_comunidad', 'problematicas_comunidad',
            'fortalezas_comunidad', 'diag_academicos', 'diag_asistencia', 'diag_rezago',
            'diag_abandono', 'diag_inclusion', 'diag_convivencia', 'diag_salud',
            'diag_infraestructura', 'diag_participacion', 'diag_evaluaciones'
        ],
        'Problematicas' => ['nombre', 'descripcion', 'id'],
        'Eventos' => ['nombre', 'tipo', 'fecha_inicio', 'fecha_fin', 'avance', 'responsable', 'notas', 'id'],
    ];

    $permitidosParaTabla = $permitidos[$tabla] ?? [];
    foreach ($campos as $campo) {
        if (!in_array($campo, $permitidosParaTabla, true)) {
            throw new Exception("Campo no permitido: $campo en tabla $tabla");
        }
    }
}
