const express = require('express');
const db = require('../db/models');
const wrapAsync = require('../helpers').wrapAsync;

const router = express.Router();
const MAX_SQL_LENGTH = 5000;
const MAX_SQL_ROWS = 1000;
const SQL_TIMEOUT_MS = 5000;

const validateReadOnlyQuery = (sql) => {
  if (typeof sql !== 'string' || !sql.trim()) {
    return 'SQL is required';
  }

  if (sql.length > MAX_SQL_LENGTH) {
    return `SQL is too long (max ${MAX_SQL_LENGTH} characters)`;
  }

  const normalized = sql.trim().replace(/;+\s*$/, '');

  if (!/^(select|with)\b/i.test(normalized)) {
    return 'Only SELECT statements are allowed';
  }

  if (normalized.includes(';')) {
    return 'Only a single statement is allowed';
  }

  if (/--|\/\*/.test(normalized)) {
    return 'SQL comments are not allowed';
  }

  if (/\b(pg_sleep|set_config|copy)\b/i.test(normalized)) {
    return 'Restricted SQL function detected';
  }

  return null;
};

/**
 * @swagger
 * /api/sql:
 *  post:
 *    security:
 *      - bearerAuth: []
 *    summary: Execute a SELECT-only SQL query
 *    description: Executes a read-only SQL query and returns rows.
 *    requestBody:
 *      required: true
 *      content:
 *        application/json:
 *          schema:
 *            type: object
 *            properties:
 *              sql:
 *                type: string
 *            required:
 *              - sql
 *    responses:
 *      200:
 *        description: Query result
 *      400:
 *        description: Invalid SQL
 *      401:
 *        $ref: "#/components/responses/UnauthorizedError"
 *      500:
 *        description: Internal server error
 */
router.post(
  '/',
  wrapAsync(async (req, res) => {
    const { currentUser } = req;
    const isAdminUser = Boolean(
      currentUser &&
      currentUser.app_role &&
      (currentUser.app_role.name === 'Administrator' ||
        currentUser.app_role.globalAccess === true),
    );

    if (!isAdminUser) {
      return res
        .status(403)
        .json({ error: 'Only administrators can execute SQL queries' });
    }

    const { sql } = req.body;
    const validationError = validateReadOnlyQuery(sql);
    if (validationError) {
      return res.status(400).json({ error: validationError });
    }

    const normalized = sql.trim().replace(/;+\s*$/, '');
    const wrappedSql = `SELECT * FROM (${normalized}) AS query_result LIMIT ${MAX_SQL_ROWS}`;

    const rows = await db.sequelize.transaction(async (transaction) => {
      await db.sequelize.query(
        `SET LOCAL statement_timeout = ${SQL_TIMEOUT_MS}`,
        { transaction },
      );
      return db.sequelize.query(wrappedSql, {
        transaction,
        type: db.Sequelize.QueryTypes.SELECT,
      });
    });

    return res.status(200).json({
      rows,
      meta: {
        maxRows: MAX_SQL_ROWS,
        statementTimeoutMs: SQL_TIMEOUT_MS,
      },
    });
  }),
);

module.exports = router;
