Source: Router/maintenance.js

// @ts-check
/**
 * @import {ExpressRequestAuthorized, ExpressResponse} from '../types.js'
 */
/**
 * Maintenance Router
 * 
 * This router defines all endpoints for maintenance operations:
 * - Object recreation and template updates
 * - Database optimization and cleanup
 * - Tree rebuilding and access management
 * - Achievement processing
 * - Family and person data management
 * - Event management and participant lists
 * - System maintenance tasks
 * - SEPA/Lastschrift processing
 * 
 * All endpoints require admin rights unless otherwise specified.
 * 
 * @swagger
 * tags:
 *   - name: Maintenance
 *     description: |
 *       Administrative maintenance operations for the organisation database
 * 
 *       Schemas and examples are defined in ./maintenance/maintenance.swagger.yaml.
 *       Use $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/{SchemaName}' in route blocks.
 */
import express from 'express';
import { checkAdmin, checkEmployee } from '../utils/authChecks.js';
import { validateSingleUID } from '../utils/uuidValidation.js';
import {
    createDB,
    recreateObjects,
    getTree,
    migrateDB,
    getAchievements,
    getAchievementsDuplicates,
    getAchievementsTree,
    requalifyPersons,
    triggerMaintenance,
    getFamilyData,
    checkFamilies,
    getVisibility,
    optimizeDatabase,
    getEventLog,
    rebuildListEntries,
    createBirthdayFilter,
    cleanFilters,
    getEventVisibility,
    getEventParticipantLists,
    deleteEvents,
    restorePerson,
    processLastschrift,
    regenerateEmbeddings,
    rebuildSearchIndex,
    compressHistory,
    rebuildAccountingVisibility
} from './maintenance/controller.js';
import { personConsistency } from './maintenance/personConsistency.js';
import { memberAConsistency } from './maintenance/memberAConsistency.js';

/** @type {express.Express} */
const api = express();

/**
 * @swagger
 * /maintenance/recreate/{type}:
 *   get:
 *     summary: Recreate all objects of a given type via template
 *     description: |
 *       Re-renders all objects of the specified type in the current organisation using the configured
 *       template. Updates Title, SortName, Display, FullTextIndex, stage, gender, and hierarchie
 *       fields as needed. Supports person, extern, guest, job, function, event, location and other types.
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: type
 *         required: true
 *         schema:
 *           type: string
 *           enum: [person, extern, guest, job, function, event, location, group, list, dlist, email]
 *         description: Object type to recreate
 *         example: person
 *     responses:
 *       200:
 *         description: Recreate completed successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/recreate/:type', checkAdmin, /** @type {any} */ (recreateObjects) );

/**
 * @swagger
 * /maintenance/tree/{identifyer}:
 *   get:
 *     summary: Rebuild the membership tree
 *     description: |
 *       Performs a full diff-based rebuild of the membership (ownership) tree for the specified
 *       identifier. Deletes stale links and inserts missing ones. Valid identifiers are object type
 *       keywords (person, group, ggroup, guest, extern, event) or a specific UUID.
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: identifyer
 *         required: true
 *         schema:
 *           type: string
 *         description: Object type keyword or UUID to rebuild
 *         example: person
 *     responses:
 *       200:
 *         description: Tree rebuild completed or invalid identifier
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                 message:
 *                   type: string
 *                   description: Present only when the identifier is invalid
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/tree/:identifyer', checkAdmin, /** @type {any} */ (getTree) );

/**
 * @swagger
 * /maintenance/migrateDB:
 *   get:
 *     summary: Run pending database migrations
 *     description: |
 *       Applies all pending migration modules found in src/config/migrations/.
 *       Each migration supports SQL schema changes and/or JSON data transformations.
 *       Applied migrations are tracked in the dbVersion table.
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Migration completed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 migrationsApplied:
 *                   type: number
 *                   description: Number of migrations that were applied
 *                   example: 2
 *                 migrationsSkipped:
 *                   type: number
 *                   description: Number of migrations already up to date
 *                   example: 5
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/migrateDB', checkAdmin, /** @type {any} */ (migrateDB) );

/**
 * @swagger
 * /maintenance/optimize:
 *   get:
 *     summary: Optimize and analyze all database tables
 *     description: |
 *       Runs OPTIMIZE TABLE and ANALYZE TABLE on every base table in the current database.
 *       Helps reclaim storage and keep query statistics up to date.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Optimization completed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/optimize', /** @type {any} */ (optimizeDatabase) );

/**
 * @swagger
 * /maintenance/achievements:
 *   get:
 *     summary: Extract and return achievements for the organisation
 *     description: Returns all achievements extracted via the achievement template logic for the current organisation.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of achievements
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 type: object
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/achievements', checkAdmin, /** @type {any} */ (getAchievements) );

/**
 * @swagger
 * /maintenance/achievements/duplicates:
 *   get:
 *     summary: Find duplicate achievement objects
 *     description: Returns pairs of achievement objects that share the same Title within the organisation.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of duplicate achievement pairs
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 type: object
 *                 properties:
 *                   UID:
 *                     type: string
 *                     format: uuid
 *                   Title:
 *                     type: string
 *                   duplicateUID:
 *                     type: string
 *                     format: uuid
 *                   duplicateTitle:
 *                     type: string
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/achievements/duplicates', checkAdmin, /** @type {any} */ (getAchievementsDuplicates) );

/**
 * @swagger
 * /maintenance/achievements/tree:
 *   get:
 *     summary: Get the achievement-to-template tree
 *     description: Returns a flat list of achievement objects linked to their achievement templates, ordered by owner (UIDBelongsTo).
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Achievement tree
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 tree:
 *                   type: array
 *                   items:
 *                     type: object
 *                     properties:
 *                       UID:
 *                         type: string
 *                         format: uuid
 *                       UIDBelongsTo:
 *                         type: string
 *                         format: uuid
 *                       validFrom:
 *                         type: number
 *                         description: UNIX timestamp
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/achievements/tree', /** @type {any} */ (getAchievementsTree) );

/**
 * @swagger
 * /maintenance/requalify:
 *   get:
 *     summary: Recheck qualifications for all job functions
 *     description: |
 *       Iterates over all function objects in the organisation and re-evaluates the qualification
 *       status of every linked job. Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Requalification completed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 processedFunctions:
 *                   type: number
 *                   description: Number of function objects processed
 *                   example: 12
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/requalify', /** @type {any} */ (requalifyPersons) );

/**
 * @swagger
 * /maintenance/trigger:
 *   get:
 *     summary: Trigger the organisation tree queue
 *     description: Resets the queue-running flag for the organisation and triggers all pending tree queues.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Queue triggered
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 message:
 *                   type: string
 *                   example: Tree queue triggered
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/trigger', checkAdmin, /** @type {any} */ (triggerMaintenance) );

/**
 * @swagger
 * /maintenance/family/{UID}:
 *   get:
 *     summary: Process and correct family link types
 *     description: |
 *       Retrieves family objects and fixes incorrect link types between person/extern objects and
 *       family objects (family ↔ familyFees). Use `all` to process every family in the organisation,
 *       or supply a specific family UUID.
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: UID
 *         required: true
 *         schema:
 *           type: string
 *         description: Family UUID or the keyword `all`
 *         example: all
 *     responses:
 *       200:
 *         description: Family data processed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 processed:
 *                   type: number
 *                   description: Number of corrected links
 *                   example: 3
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/family/:UID', checkAdmin, /** @type {any} */ (getFamilyData) );

/**
 * @swagger
 * /maintenance/familyCheck:
 *   get:
 *     summary: Check family last-name consistency
 *     description: Returns a list of families where members have differing last names, indicating possible data inconsistencies.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: List of inconsistent families
 *         content:
 *           application/json:
 *             schema:
 *               type: array
 *               items:
 *                 type: object
 *                 properties:
 *                   UIDfamily:
 *                     type: string
 *                     format: uuid
 *                   Display:
 *                     type: string
 *                   lastName:
 *                     type: string
 *                   members:
 *                     type: array
 *                     items:
 *                       type: string
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/familyCheck', /** @type {any} */ (checkFamilies) );

/**
 * @swagger
 * /maintenance/visibility/{UID}:
 *   get:
 *     summary: Rebuild visibility / access control entries
 *     description: |
 *       Rebuilds Visible table entries for the specified object(s).
 *       - `lists` – all list/dlist/email/event/eventT/location objects
 *       - `job`   – all job objects (person and list access)
 *       - UUID    – single object identified by UID
 *       An optional `types` query parameter (comma-separated) overrides the UID-based selection.
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: UID
 *         required: true
 *         schema:
 *           type: string
 *         description: Keyword (`lists`, `job`) or a specific object UUID
 *         example: lists
 *       - in: query
 *         name: types
 *         required: false
 *         schema:
 *           type: string
 *         description: Comma-separated list of object types to process (job, list, dlist, email, event, eventT, location)
 *         example: list,dlist
 *     responses:
 *       200:
 *         description: Visibility rebuild completed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       400:
 *         description: No valid types supplied
 *       401:
 *         description: Unauthorized - Admin rights required
 *       404:
 *         description: Object not found (when a UUID is supplied)
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/visibility/:UID',checkAdmin, /** @type {any} */ (getVisibility) );

/**
 * @swagger
 * /maintenance/eventLog:
 *   get:
 *     summary: Clean up old event log entries
 *     description: |
 *       Deletes eventLog entries older than the provided UNIX timestamp.
 *       If no timestamp is given, entries older than the first day of the previous month are removed.
 *       The table is optimised after deletion.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: timestamp
 *         required: false
 *         schema:
 *           type: number
 *         description: UNIX timestamp cutoff – entries older than this are deleted
 *         example: 1700000000
 *     responses:
 *       200:
 *         description: Event log cleaned
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/eventLog', /** @type {any} */ (getEventLog) );

/**
 * @swagger
 * /maintenance/rebuildListEntries/{UID}:
 *   get:
 *     summary: Rebuild dynamic list memberships for a person or all persons
 *     description: |
 *       First removes dangling list-entry links, then re-evaluates list memberships for
 *       the specified person UUID or all persons/externs in the organisation.
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: UID
 *         required: true
 *         schema:
 *           type: string
 *         description: Person UUID or the keyword `all`
 *         example: all
 *     responses:
 *       200:
 *         description: List entries rebuilt
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/rebuildListEntries/:UID', checkAdmin, /** @type {any} */ (rebuildListEntries) );

/**
 * @swagger
 * /maintenance/birthdayFilter:
 *   get:
 *     summary: Rebuild time-based (birthday) filters
 *     description: Re-evaluates all time-based filter rules (e.g. birthday filters) across the organisation.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Birthday filters rebuilt
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/birthdayFilter', /** @type {any} */ (createBirthdayFilter) );

/**
 * @swagger
 * /maintenance/cleanFilter:
 *   get:
 *     summary: Remove orphaned filter objects
 *     description: |
 *       Deletes ObjectBase entries of type include/exclude/intersect and visible/changeable that
 *       are no longer referenced by any dynamic list link, freeing up stale filter objects.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Filters cleaned
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 filter:
 *                   type: object
 *                   description: Result of the filter deletion query
 *                 vfilter:
 *                   type: object
 *                   description: Result of the visibility filter deletion query
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/cleanFilter', checkAdmin, /** @type {any} */ (cleanFilters) );

/**
 * @swagger
 * /maintenance/rebuildAccountingVisibility:
 *   get:
 *     summary: Rebuild accounting visibility for all functions
 *     description: |
 *       Iterates over all function templates in the organisation and re-evaluates
 *       the Visible.Accounting column for every linked job based on financialMaster
 *       flags. Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: Accounting visibility rebuild completed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 processedFunctions:
 *                   type: number
 *                   description: Number of function objects processed
 *                   example: 12
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/rebuildAccountingVisibility', checkAdmin, /** @type {any} */ (rebuildAccountingVisibility));

/**
 * @swagger
 * /maintenance/eventVisiblity/{UID}:
 *   get:
 *     summary: Rebuild event visibility / access control
 *     description: |
 *       Rebuilds the Visible table entries for the specified event or all events in the organisation.
 *       Note: the path contains the legacy typo `Visiblity` (single `i`).
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: UID
 *         required: true
 *         schema:
 *           type: string
 *         description: Event UUID or the keyword `all`
 *         example: all
 *     responses:
 *       200:
 *         description: Event visibility rebuilt
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 message:
 *                   type: string
 *                   description: Present only on validation errors
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/eventVisiblity/:UID',  /** @type {any} */ (getEventVisibility) );

/**
 * @swagger
 * /maintenance/event/participantLists/{UID}:
 *   get:
 *     summary: Create or repair participant lists for events
 *     description: |
 *       Ensures that each event has a main participant dlist and per-group participant lists.
 *       Missing lists are created automatically. Supply a specific event UUID or `all` to process
 *       every event in the organisation.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: UID
 *         required: true
 *         schema:
 *           type: string
 *         description: Event UUID or the keyword `all`
 *         example: all
 *     responses:
 *       200:
 *         description: Participant lists created/verified
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 message:
 *                   type: string
 *                   description: Present only on validation errors
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/event/participantLists/:UID', /** @type {any} */ (getEventParticipantLists) );

/**
 * @swagger
 * /maintenance/events:
 *   delete:
 *     summary: Delete all events
 *     description: |
 *       Permanently deletes every event object together with its links and member data.
 *       **This operation is irreversible.**
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: All events deleted
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.delete('/events', /** @type {any} */ (deleteEvents) );

/**
 * @swagger
 * /maintenance/restorePerson/{UID}/{timestamp}:
 *   get:
 *     summary: Restore a deleted person from historical data
 *     description: |
 *       Uses MariaDB system-versioned tables to locate and re-insert person/extern objects and their
 *       links as they existed at the given UNIX timestamp. Also restores visibility entries.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: UID
 *         required: true
 *         schema:
 *           type: string
 *           format: uuid
 *         description: UUID of the person to restore
 *       - in: path
 *         name: timestamp
 *         required: true
 *         schema:
 *           type: number
 *         description: UNIX timestamp of the historical state to restore from
 *         example: 1700000000
 *     responses:
 *       200:
 *         description: Restore result (success or not-found)
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                 message:
 *                   type: string
 *                   description: Present when the person was not found at the given timestamp
 *                 insertResults:
 *                   type: object
 *                   properties:
 *                     objects:
 *                       type: object
 *                       nullable: true
 *                     links:
 *                       type: object
 *                       nullable: true
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/restorePerson/:UID/:timestamp', validateSingleUID, /** @type {any} */ (restorePerson) );

/**
 * @swagger
 * /maintenance/lastschrift:
 *   get:
 *     summary: Process SEPA Lastschrift data
 *     description: |
 *       Assigns SEPA mandate numbers to familyFees accounts that are missing them, and
 *       normalises accounts without an IBANowner from `familyFees` to `family`.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     responses:
 *       200:
 *         description: SEPA data processed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/lastschrift/', /** @type {any} */ (processLastschrift) );

/**
 * @swagger
 * /maintenance/embeddings/{type}:
 *   get:
 *     summary: Regenerate AI embeddings for a given object type
 *     description: |
 *       Clears and rebuilds the AIEmbeddings table entries for all objects of the specified type
 *       in the current organisation. Objects are processed in batches of 50 with a single API call
 *       per batch. Already up-to-date embeddings (same content hash) are skipped.
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: type
 *         required: true
 *         schema:
 *           type: string
 *           enum: [person, extern, guest, job]
 *         description: Object type to regenerate embeddings for
 *         example: person
 *     responses:
 *       200:
 *         description: Embeddings regenerated
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 processed:
 *                   type: number
 *                   description: Total objects processed
 *                 succeeded:
 *                   type: number
 *                 failed:
 *                   type: number
 *                 model:
 *                   type: string
 *                   description: Embedding model used
 *       400:
 *         description: Invalid type parameter
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/embeddings/:type',  /** @type {any} */ (regenerateEmbeddings));

// Search index rebuild
/**
 * @swagger
 * /maintenance/searchindex/{type}:
 *   get:
 *     summary: Rebuild search index for object type
 *     description: |
 *       Clears and rebuilds the SearchIndex table entries (email, phone, etc.) 
 *       for all objects of the specified type in the current organization.
 *       Protected by admin rights.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: path
 *         name: type
 *         required: true
 *         schema:
 *           type: string
 *           enum: [person, extern, guest, all]
 *         description: Object type to rebuild search index for
 *         example: "person"
 *     responses:
 *       200:
 *         description: Search index rebuilt successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 message:
 *                   type: string
 *                   example: "Search index rebuilt for person"
 *                 processed:
 *                   type: number
 *                   description: Number of objects processed
 *                   example: 150
 *                 emailsInserted:
 *                   type: number
 *                   description: Number of email index entries created
 *                   example: 220
 *                 phonesInserted:
 *                   type: number
 *                   description: Number of phone index entries created
 *                   example: 180
 *                 failed:
 *                   type: number
 *                   description: Number of objects that failed processing
 *                   example: 0
 *       400:
 *         description: Invalid type parameter
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/searchindex/:type', checkAdmin, /** @type {any} */ (rebuildSearchIndex));

/**
 * @swagger
 * /maintenance/compressHistory:
 *   get:
 *     summary: Compress history of system-versioned tables
 *     description: |
 *       Reduces the history size of system-versioned tables (ObjectBase, Links) by
 *       removing consecutive duplicate history entries. Each unique entity is examined:
 *       only the first occurrence and rows where data changes are kept; identical
 *       intermediate history rows are dropped.
 *
 *       **Workflow:**
 *       1. `GET /maintenance/compressHistory` (dry-run) — reports how many rows would be saved.
 *       2. `GET /maintenance/compressHistory?apply=true` — creates `_new` tables with compressed data.
 *       3. `GET /maintenance/compressHistory?apply=true&swap=true` — also performs the RENAME swap,
 *          replacing the original tables with the compressed ones (old tables become `_oldbak`).
 *
 *       Sends progress events via WebSocket.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: apply
 *         required: false
 *         schema:
 *           type: string
 *           enum: [true]
 *         description: Set to `true` to actually create compressed tables (default: dry-run)
 *       - in: query
 *         name: swap
 *         required: false
 *         schema:
 *           type: string
 *           enum: [true]
 *         description: Set to `true` to perform the RENAME swap (requires `apply=true`)
 *       - in: query
 *         name: chunkSize
 *         required: false
 *         schema:
 *           type: integer
 *           default: 50000
 *         description: Number of history rows to process per chunk
 *     responses:
 *       200:
 *         description: Compression completed
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 dryRun:
 *                   type: boolean
 *                   description: Whether this was a dry run (no actual changes)
 *                 swapPerformed:
 *                   type: boolean
 *                   description: Whether the RENAME swap was performed
 *                 results:
 *                   type: array
 *                   items:
 *                     type: object
 *                     properties:
 *                       table:
 *                         type: string
 *                         description: Table name
 *                       squeezed:
 *                         type: number
 *                         description: Number of history rows removed
 *                       inserted:
 *                         type: number
 *                         description: Number of rows inserted into _new table
 *                       originalTotal:
 *                         type: number
 *                         description: Total rows in original table (FOR SYSTEM_TIME ALL)
 *                       newTotal:
 *                         type: number
 *                         description: Total rows in compressed table (FOR SYSTEM_TIME ALL)
 *       401:
 *         description: Unauthorized - Admin rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/compressHistory', checkAdmin, /** @type {any} */ (compressHistory));

/**
 * @swagger
 * /maintenance/personConsistency:
 *   get:
 *     summary: Report (and optionally repair) person level drift
 *     description: |
 *       The membership level of a person/extern is stored twice: as versioned columns
 *       (`ObjectBase.stage`, `ObjectBase.hierarchie`), which drive the rendered `Title`,
 *       and inside the non-versioned persona payload (`Member.Data.stage`,
 *       `Member.Data.hierarchie`), which the UI reads for the stage colour and which
 *       `{"Data.stage": N}` filters, lists and bots match on. The primary group
 *       assignment lives in the `memberA` link.
 *
 *       Transfers that route through `updateGroupMembershipShared` keep all three in
 *       sync. Transfers that bypass it (tree queue, list/DList sync, ActionBots,
 *       imports) can leave two classes of drift behind:
 *
 *       1. `Member.Data.stage` keeps the level of the group the person left, so the
 *          persona still shows the previous level.
 *       2. A surplus `memberA` link keeps the person listed in the group they left.
 *
 *       Without `fix` the endpoint is read-only and reports both classes. With
 *       `fix=true` it aligns the persona payload with `ObjectBase` (the authoritative
 *       level). Surplus `memberA` links are NOT repaired here — `memberAConsistency`
 *       owns that, for every object type, with the single rule "the later link wins".
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: fix
 *         required: false
 *         description: Apply the repairs instead of only reporting them.
 *         schema:
 *           type: boolean
 *           default: false
 *     responses:
 *       200:
 *         description: Report, plus the applied repairs when `fix=true`
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 fix:
 *                   type: boolean
 *                   description: Whether repairs were applied
 *                 result:
 *                   type: object
 *                   properties:
 *                     checked:
 *                       type: integer
 *                       description: Number of persons/externs inspected
 *                     stageMismatch:
 *                       type: array
 *                       description: Persona payload level differs from the ObjectBase level
 *                       items:
 *                         type: object
 *                     multiplePrimaryGroups:
 *                       type: array
 *                       description: Persons holding more than one memberA group link
 *                       items:
 *                         type: object
 *                     missingPrimaryGroup:
 *                       type: array
 *                       description: Persons without any memberA group link
 *                       items:
 *                         type: object
 *                     repairedStage:
 *                       type: array
 *                       description: Persona payloads aligned with ObjectBase (fix only)
 *                       items:
 *                         type: object
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/personConsistency', checkAdmin, /** @type {any} */ (personConsistency) );

/**
 * @swagger
 * /maintenance/memberAConsistency:
 *   get:
 *     summary: Report (and optionally repair) duplicate or dangling memberA links
 *     description: |
 *       Enforces the invariant "exactly one ACTIVE `memberA` link per object" for
 *       every object type. The invariant is guarded for new links by the trigger
 *       installed with migration `20260925-links-single-membera-trigger`; this
 *       endpoint repairs the rows that predate it.
 *
 *       Resolution rule: a transfer inserts the new link and fails to close the
 *       previous one, so **the chronologically later link (`ValidFrom`) is the
 *       intention** and the older surplus links are removed. `entry` objects
 *       (dynamic list memberships) are resolved by their `dynamic` reference
 *       instead: only a `memberA` link with a `dynamic` link to the same target is
 *       kept. Genuine ties and links of deleted source objects are reported under
 *       `skipped` and left untouched.
 *
 *       One case link removal must NOT decide: an `entry` corroborated by several
 *       `dynamic` links carries the membership of several dlists. The links are not
 *       surplus — removing one would silently drop the person from a list. Such
 *       objects are reported under `listEntriesToRebuild` (instead of `skipped`) and,
 *       with `fix=true`, split by `rebuildListEntries` so that every list gets its own
 *       `entry` object; the outcome is reported under `rebuiltListEntries`.
 *
 *       `Links` is system-versioned, so removal is a `DELETE` of the current row —
 *       the history stays inspectable via `FOR SYSTEM_TIME ALL`.
 *
 *       The endpoint only touches links. `ObjectBase.stage`/`hierarchie`, the
 *       rendered `Title` and `Member.Data` are left alone; run `recreateObjects`
 *       afterwards for those (see `needsRecreate` and `flaggedObjectBase`).
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     parameters:
 *       - in: query
 *         name: fix
 *         required: false
 *         description: Remove the surplus links instead of only reporting them.
 *         schema:
 *           type: boolean
 *           default: false
 *     responses:
 *       200:
 *         description: Report, plus the removed links when `fix=true`
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 fix:
 *                   type: boolean
 *                   description: Whether links were removed
 *                 result:
 *                   type: object
 *                   properties:
 *                     checked:
 *                       type: integer
 *                       description: Number of objects with suspicious memberA links
 *                     duplicates:
 *                       type: array
 *                       description: Objects holding more than one active memberA link
 *                       items:
 *                         type: object
 *                     dangling:
 *                       type: array
 *                       description: Active memberA links whose target no longer exists
 *                       items:
 *                         type: object
 *                     linksOfDeletedObjects:
 *                       type: array
 *                       description: Links whose source object was deleted (deletion debris) — removed by the repair
 *                       items:
 *                         type: object
 *                     wouldRemove:
 *                       type: array
 *                       description: Links the repair would remove — populated in dry-run mode too
 *                       items:
 *                         type: object
 *                     removedLinks:
 *                       type: array
 *                       description: Removed links (fix only)
 *                       items:
 *                         type: object
 *                     listEntriesToRebuild:
 *                       type: array
 *                       description: |
 *                         `entry` objects carrying the membership of several dlists.
 *                         They are split rather than trimmed; populated in dry-run mode too.
 *                       items:
 *                         type: object
 *                     rebuiltListEntries:
 *                       type: array
 *                       description: Outcome of the entry splits, one per person (fix only)
 *                       items:
 *                         type: object
 *                     flaggedObjectBase:
 *                       type: array
 *                       description: Kept link matches no ObjectBase level — check before recreating
 *                       items:
 *                         type: object
 *                     needsRecreate:
 *                       type: array
 *                       description: ObjectBase.stage and hierarchie are both 0 (uninitialised)
 *                       items:
 *                         type: object
 *                     skipped:
 *                       type: array
 *                       description: Cases that need a human decision
 *                       items:
 *                         type: object
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.get('/memberAConsistency', checkAdmin, /** @type {any} */ (memberAConsistency) );

// Create a new database
/**
 * @swagger
 * /maintenance/create-db:
 *   post:
 *     summary: Create a new database
 *     description: |
 *       Creates a new database schema with all required tables, views, and functions.
 *       Protected by employee rights.
 *     tags:
 *       - Maintenance
 *     security:
 *       - bearerAuth: []
 *     requestBody:
 *       required: true
 *       content:
 *         application/json:
 *           schema:
 *             type: object
 *             required:
 *               - database
 *             properties:
 *               database:
 *                 type: string
 *                 description: Name of the database to create
 *                 example: "tenant_db_123"
 *     responses:
 *       200:
 *         description: Database created successfully
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 success:
 *                   type: boolean
 *                   example: true
 *                 message:
 *                   type: string
 *                   example: "Database created successfully"
 *                 database:
 *                   type: string
 *                   description: Name of the created database
 *       401:
 *         description: Unauthorized - Employee rights required
 *       500:
 *         description: Internal server error
 *         content:
 *           application/json:
 *             schema:
 *               $ref: './maintenance/maintenance.swagger.yaml#/components/schemas/Error'
 */
// @ts-ignore
api.post('/create-db', checkEmployee, /** @type {any} */ (createDB));

export default api;