const archiver =
  require("archiver");

const {
  pool
} = require("../config/db");

const {
  writeAdminAuditLog
} = require(
  "../services/adminAuditService"
);

const {
  approvedStatus,
  createStaffRecordFileName,
  createStaffRecordPdf,
  createStaffRecordsWorkbook,
  loadStaffRecord,
  loadStaffRecordsForExcel,
  normalizePositiveInteger,
  sanitizeFileName
} = require(
  "../services/staffRecordExportService"
);

const allowedRoles =
  new Set([
    "super_admin",
    "hr_admin",
    "hr_reviewer"
  ]);

const getRequestIp = (
  request
) => {
  return (
    request.ip ||
    request.socket
      ?.remoteAddress ||
    null
  );
};

const getUserAgent = (
  request
) => {
  return (
    request.get(
      "user-agent"
    )?.slice(
      0,
      500
    ) || null
  );
};

const sendError = (
  response,
  error,
  fallbackMessage
) => {
  return response
    .status(500)
    .json({
      success: false,

      message:
        process.env
          .NODE_ENV !==
        "production"
          ? error
              .sqlMessage ||
            error.message ||
            fallbackMessage
          : fallbackMessage
    });
};

const verifyAccess = (
  request,
  record
) => {
  if (
    !allowedRoles.has(
      request.admin?.role
    )
  ) {
    return {
      allowed:
        false,

      status:
        403,

      message:
        "You are not authorised to export staff records."
    };
  }

  if (
    request.admin.role ===
    "hr_reviewer"
  ) {
    const assignedTo =
      Number(
        record.submission
          .assigned_to
      );

    if (
      assignedTo !==
      Number(
        request.admin.id
      )
    ) {
      return {
        allowed:
          false,

        status:
          403,

        message:
          "This submission is not assigned to you."
      };
    }
  }

  return {
    allowed:
      true
  };
};

const setFileHeaders = (
  response,
  contentType,
  fileName,
  disposition =
    "attachment"
) => {
  response.setHeader(
    "Content-Type",
    contentType
  );

  response.setHeader(
    "Content-Disposition",
    `${
      disposition ===
      "inline"
        ? "inline"
        : "attachment"
    }; filename="${sanitizeFileName(
      fileName,
      "export"
    )}"`
  );

  response.setHeader(
    "Cache-Control",
    "private, no-store, max-age=0"
  );

  response.setHeader(
    "Pragma",
    "no-cache"
  );
};

const auditExport =
  async (
    request,
    values
  ) => {
    return writeAdminAuditLog({
      adminUserId:
        request.admin.id,

      ipAddress:
        getRequestIp(
          request
        ),

      userAgent:
        getUserAgent(
          request
        ),

      ...values
    });
  };

/* =========================================================
   INDIVIDUAL STAFF RECORD PDF

   GET /api/admin/submissions/:submissionId/export/pdf
   ========================================================= */

const exportSubmissionPdf =
  async (
    request,
    response
  ) => {
    let connection = null;

    try {
      const submissionId =
        normalizePositiveInteger(
          request.params
            .submissionId
        );

      if (!submissionId) {
        return response
          .status(422)
          .json({
            success:
              false,

            message:
              "A valid submission is required."
          });
      }

      connection =
        await pool
          .getConnection();

      const record =
        await loadStaffRecord(
          connection,
          submissionId
        );

      if (!record) {
        return response
          .status(404)
          .json({
            success:
              false,

            message:
              "The staff submission could not be found."
          });
      }

      const access =
        verifyAccess(
          request,
          record
        );

      if (!access.allowed) {
        return response
          .status(
            access.status
          )
          .json({
            success:
              false,

            message:
              access.message
          });
      }

      const official =
        record.submission
          .submission_status ===
        approvedStatus;

      const disposition =
        request.query
          .disposition ===
        "inline"
          ? "inline"
          : "attachment";

      const pdfBuffer =
        await createStaffRecordPdf(
          record,
          {
            official
          }
        );

      const fileName =
        createStaffRecordFileName(
          record,
          "pdf"
        );

      await auditExport(
        request,
        {
          action:
            disposition ===
            "inline"
              ? "STAFF_RECORD_PDF_PREVIEWED"
              : "STAFF_RECORD_PDF_DOWNLOADED",

          entityType:
            "staff_submission",

          entityId:
            submissionId,

          description:
            `${
              official
                ? "Official"
                : "Draft"
            } staff record PDF exported for ${
              record
                .submission
                .submission_reference
            }.`,

          metadata: {
            submission_reference:
              record
                .submission
                .submission_reference,

            staff_number:
              record
                .submission
                .staff_number,

            staff_name:
              record
                .submission
                .full_name,

            official,
            disposition
          }
        }
      );

      setFileHeaders(
        response,
        "application/pdf",
        fileName,
        disposition
      );

      response.setHeader(
        "Content-Length",
        pdfBuffer.length
      );

      return response.send(
        pdfBuffer
      );
    } catch (error) {
      console.error(
        "Export submission PDF error:",
        error
      );

      return sendError(
        response,
        error,
        "Unable to export the staff record PDF."
      );
    } finally {
      if (connection) {
        connection.release();
      }
    }
  };

/* =========================================================
   BULK EXCEL EXPORT

   GET /api/admin/submissions/export/excel
   ========================================================= */

const exportSubmissionsExcel =
  async (
    request,
    response
  ) => {
    let connection = null;

    try {
      connection =
        await pool
          .getConnection();

      const filters = {
        search:
          request.query
            .search || "",

        status:
          request.query
            .status || "",

        assignment:
          request.query
            .assignment ||
          "all",

        reviewerId:
          request.query
            .reviewer_id ||
          ""
      };

      const records =
        await loadStaffRecordsForExcel(
          connection,
          filters,
          request.admin
        );

      const workbookBuffer =
        await createStaffRecordsWorkbook(
          records
        );

      const date =
        new Date()
          .toISOString()
          .slice(
            0,
            10
          );

      await auditExport(
        request,
        {
          action:
            "STAFF_RECORD_BULK_EXPORTED",

          entityType:
            "staff_submission",

          entityId:
            null,

          description:
            `${records.length} staff record(s) exported to Excel.`,

          metadata: {
            record_count:
              records.length,

            filters
          }
        }
      );

      setFileHeaders(
        response,

        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",

        `CAGD_Staff_Records_${date}.xlsx`
      );

      response.setHeader(
        "Content-Length",
        workbookBuffer.length
      );

      return response.send(
        workbookBuffer
      );
    } catch (error) {
      console.error(
        "Export submissions Excel error:",
        error
      );

      return sendError(
        response,
        error,
        "Unable to export the staff records workbook."
      );
    } finally {
      if (connection) {
        connection.release();
      }
    }
  };

/* =========================================================
   APPROVED RECORD PDF ZIP

   POST /api/admin/submissions/export/pdf-batch
   ========================================================= */

const exportSubmissionPdfBatch =
  async (
    request,
    response
  ) => {
    let connection = null;

    try {
      const submittedIds =
        Array.isArray(
          request.body
            ?.submission_ids
        )
          ? request.body
              .submission_ids
          : [];

      const submissionIds = [
        ...new Set(
          submittedIds
            .map(
              normalizePositiveInteger
            )
            .filter(Boolean)
        )
      ];

      if (
        submissionIds.length ===
        0
      ) {
        return response
          .status(422)
          .json({
            success:
              false,

            message:
              "Select at least one approved staff record."
          });
      }

      if (
        submissionIds.length >
        100
      ) {
        return response
          .status(422)
          .json({
            success:
              false,

            message:
              "A maximum of 100 staff records may be exported in one PDF batch."
          });
      }

      connection =
        await pool
          .getConnection();

      const records = [];

      for (
        const submissionId of
        submissionIds
      ) {
        const record =
          await loadStaffRecord(
            connection,
            submissionId
          );

        if (!record) {
          return response
            .status(404)
            .json({
              success:
                false,

              message:
                `Submission ${submissionId} could not be found.`
            });
        }

        const access =
          verifyAccess(
            request,
            record
          );

        if (!access.allowed) {
          return response
            .status(
              access.status
            )
            .json({
              success:
                false,

              message:
                access.message
            });
        }

        if (
          record.submission
            .submission_status !==
          approvedStatus
        ) {
          return response
            .status(409)
            .json({
              success:
                false,

              message:
                `Submission ${
                  record
                    .submission
                    .submission_reference
                } is not approved and cannot be included in the official PDF batch.`
            });
        }

        records.push(
          record
        );
      }

      await auditExport(
        request,
        {
          action:
            "STAFF_RECORD_PDF_BATCH_EXPORTED",

          entityType:
            "staff_submission",

          entityId:
            null,

          description:
            `${records.length} approved staff record PDF(s) exported as a ZIP archive.`,

          metadata: {
            submission_ids:
              submissionIds,

            record_count:
              records.length
          }
        }
      );

      const date =
        new Date()
          .toISOString()
          .slice(
            0,
            10
          );

      setFileHeaders(
        response,
        "application/zip",
        `CAGD_Approved_Staff_Records_${date}.zip`
      );

      const archive =
        archiver(
          "zip",
          {
            zlib: {
              level:
                9
            }
          }
        );

      archive.on(
        "warning",
        (
          error
        ) => {
          if (
            error.code !==
            "ENOENT"
          ) {
            response.destroy(
              error
            );
          }
        }
      );

      archive.on(
        "error",
        (
          error
        ) => {
          response.destroy(
            error
          );
        }
      );

      archive.pipe(
        response
      );

      for (
        const record of
        records
      ) {
        const pdfBuffer =
          await createStaffRecordPdf(
            record,
            {
              official:
                true
            }
          );

        archive.append(
          pdfBuffer,
          {
            name:
              createStaffRecordFileName(
                record,
                "pdf"
              )
          }
        );
      }

      await archive.finalize();

      return undefined;
    } catch (error) {
      console.error(
        "Export submission PDF batch error:",
        error
      );

      if (
        response.headersSent
      ) {
        response.destroy(
          error
        );

        return undefined;
      }

      return sendError(
        response,
        error,
        "Unable to export the approved staff-record PDF batch."
      );
    } finally {
      if (connection) {
        connection.release();
      }
    }
  };

module.exports = {
  exportSubmissionPdf,
  exportSubmissionPdfBatch,
  exportSubmissionsExcel
};