import { spawn } from "node:child_process";

import { env } from "../config/env.js";
import { pool } from "../db/pool.js";
import { ApiError } from "../utils/ApiError.js";
import { logAudit } from "../utils/auditLog.js";
import { canExportBackup, fetchRolePermissions } from "../utils/permissions.js";
import { ensureSystemSettings, updateSystemBackupMetadata } from "../utils/systemSettings.js";

const ensureBackupPermission = async (db, req) => {
  const permissions = await fetchRolePermissions(db, req.user?.role);

  if (!canExportBackup(req.user?.role, permissions)) {
    throw new ApiError(403, "Accès refusé à la sauvegarde.");
  }
};

export const backupController = {
  exportDatabase: async (req, res) => {
    const client = await pool.connect();

    try {
      await client.query("BEGIN");
      await ensureBackupPermission(client, req);
      await ensureSystemSettings(client);
      await client.query("COMMIT");
    } catch (error) {
      await client.query("ROLLBACK");
      client.release();
      throw error;
    }

    client.release();

    const timestamp = new Date().toISOString().slice(0, 19).replaceAll(":", "-");
    const filename = `scf-backup-${timestamp}.sql`;
    const dumpProcess = spawn(
      env.pgDumpCommand,
      ["--no-owner", "--no-privileges", `--dbname=${env.dbUrl}`],
      {
        env: process.env,
      },
    );

    let sizeBytes = 0;
    let stderr = "";
    let streamStarted = false;

    dumpProcess.stdout.on("data", (chunk) => {
      sizeBytes += chunk.length;
    });

    dumpProcess.stderr.on("data", (chunk) => {
      stderr += chunk.toString();
    });

    dumpProcess.on("error", (error) => {
      if (error.code === "ENOENT") {
        res.status(503).json({
          message:
            "pg_dump est indisponible sur ce serveur. Configurez PG_DUMP_COMMAND ou installez PostgreSQL client tools.",
        });
        return;
      }

      if (!res.headersSent) {
        res.status(500).json({
          message: `Impossible de lancer pg_dump: ${error.message}`,
        });
        return;
      }

      res.destroy(error);
    });

    dumpProcess.on("spawn", () => {
      streamStarted = true;
      res.setHeader("Content-Type", "application/sql; charset=utf-8");
      res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
      dumpProcess.stdout.pipe(res);
    });

    dumpProcess.on("close", async (code) => {
      if (!streamStarted) {
        return;
      }

      if (code !== 0) {
        if (!res.headersSent) {
          res.status(500).json({
            message: stderr.trim() || "La sauvegarde a échoué.",
          });
          return;
        }

        res.destroy(new Error(stderr.trim() || "La sauvegarde a échoué."));
        return;
      }

      try {
        const auditClient = await pool.connect();

        try {
          await auditClient.query("BEGIN");
          await ensureSystemSettings(auditClient);
          const settings = await updateSystemBackupMetadata(auditClient, {
            sizeBytes,
            updatedBy: req.user?.sub || null,
          });

          await logAudit(auditClient, {
            companyId: null,
            userId: req.user?.sub || null,
            action: "backup.export",
            entityType: "system_settings",
            entityId: 1,
            newData: {
              last_backup_at: settings.last_backup_at,
              last_backup_size_bytes: settings.last_backup_size_bytes,
              filename,
            },
            ipAddress: req.ip,
          });

          await auditClient.query("COMMIT");
        } catch (error) {
          await auditClient.query("ROLLBACK");
          console.error("Backup metadata update failed", error);
        } finally {
          auditClient.release();
        }
      } catch (error) {
        console.error("Backup metadata connection failed", error);
      }
    });
  },
};
