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

/* =========================================================
   CONSTANTS
   ========================================================= */

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

/* =========================================================
   HELPERS
   ========================================================= */

const normalizePositiveInteger = (
  value,
  fallback = 0,
  maximum =
    Number.MAX_SAFE_INTEGER
) => {
  const parsedValue =
    Number(value);

  if (
    !Number.isInteger(
      parsedValue
    ) ||
    parsedValue < 1
  ) {
    return fallback;
  }

  return Math.min(
    parsedValue,
    maximum
  );
};

const normalizeText = (
  value,
  maximumLength = 255
) => {
  return String(value || "")
    .trim()
    .slice(
      0,
      maximumLength
    );
};

const normalizeDate = (
  value
) => {
  const normalizedValue =
    normalizeText(
      value,
      10
    );

  if (
    !/^\d{4}-\d{2}-\d{2}$/.test(
      normalizedValue
    )
  ) {
    return "";
  }

  const date =
    new Date(
      `${normalizedValue}T00:00:00`
    );

  if (
    Number.isNaN(
      date.getTime()
    )
  ) {
    return "";
  }

  return normalizedValue;
};

const toNumber = (
  value
) => {
  const number =
    Number(value);

  return Number.isFinite(number)
    ? number
    : 0;
};

const parseMetadata = (
  value
) => {
  if (
    value === null ||
    value === undefined ||
    value === ""
  ) {
    return null;
  }

  if (
    typeof value ===
    "object"
  ) {
    return value;
  }

  try {
    return JSON.parse(
      value
    );
  } catch {
    return {
      raw_value:
        String(value)
    };
  }
};

const sendDevelopmentError = (
  response,
  error,
  fallbackMessage
) => {
  const isDevelopment =
    process.env.NODE_ENV !==
    "production";

  return response.status(500).json({
    success: false,

    message:
      isDevelopment
        ? error.sqlMessage ||
          error.message ||
          fallbackMessage
        : fallbackMessage
  });
};

const verifyAuditAccess = (
  request,
  response
) => {
  if (!request.admin) {
    response.status(401).json({
      success: false,

      message:
        "Administrator authentication is required."
    });

    return false;
  }

  if (
    !allowedAuditRoles.has(
      request.admin.role
    )
  ) {
    response.status(403).json({
      success: false,

      message:
        "You do not have permission to view the administrator audit log."
    });

    return false;
  }

  return true;
};

/* =========================================================
   GET AUDIT LOGS
   GET /api/admin/audit-logs
   ========================================================= */

const getAdminAuditLogs =
  async (
    request,
    response
  ) => {
    try {
      if (
        !verifyAuditAccess(
          request,
          response
        )
      ) {
        return;
      }

      const page =
        normalizePositiveInteger(
          request.query.page,
          1,
          1000000
        );

      const limit =
        normalizePositiveInteger(
          request.query.limit,
          20,
          100
        );

      const offset =
        (page - 1) * limit;

      const search =
        normalizeText(
          request.query.search,
          150
        );

      const action =
        normalizeText(
          request.query.action,
          100
        );

      const entityType =
        normalizeText(
          request.query
            .entity_type,
          100
        );

      const adminUserId =
        normalizePositiveInteger(
          request.query
            .admin_user_id,
          0
        );

      const dateFrom =
        normalizeDate(
          request.query.date_from
        );

      const dateTo =
        normalizeDate(
          request.query.date_to
        );

      if (
        dateFrom &&
        dateTo &&
        dateFrom > dateTo
      ) {
        return response.status(422).json({
          success: false,

          message:
            "The start date cannot be later than the end date.",

          errors: {
            date_from:
              "Select a date on or before the end date."
          }
        });
      }

      const conditions = [
        "1 = 1"
      ];

      const parameters = [];

      if (search) {
        const searchPattern =
          `%${search}%`;

        conditions.push(
          `
            (
              audit_log.action
                LIKE ?

              OR audit_log.entity_type
                LIKE ?

              OR audit_log.description
                LIKE ?

              OR CAST(
                audit_log.entity_id
                AS CHAR
              ) LIKE ?

              OR admin_user.full_name
                LIKE ?

              OR admin_user.username
                LIKE ?

              OR admin_user.email
                LIKE ?

              OR audit_log.ip_address
                LIKE ?
            )
          `
        );

        parameters.push(
          searchPattern,
          searchPattern,
          searchPattern,
          searchPattern,
          searchPattern,
          searchPattern,
          searchPattern,
          searchPattern
        );
      }

      if (action) {
        conditions.push(
          "audit_log.action = ?"
        );

        parameters.push(
          action
        );
      }

      if (entityType) {
        conditions.push(
          "audit_log.entity_type = ?"
        );

        parameters.push(
          entityType
        );
      }

      if (adminUserId > 0) {
        conditions.push(
          "audit_log.admin_user_id = ?"
        );

        parameters.push(
          adminUserId
        );
      }

      if (dateFrom) {
        conditions.push(
          "DATE(audit_log.created_at) >= ?"
        );

        parameters.push(
          dateFrom
        );
      }

      if (dateTo) {
        conditions.push(
          "DATE(audit_log.created_at) <= ?"
        );

        parameters.push(
          dateTo
        );
      }

      const whereClause =
        conditions.join(
          "\nAND "
        );

      const joins = `
        FROM admin_audit_logs
          AS audit_log

        LEFT JOIN admin_users
          AS admin_user
          ON admin_user.id =
            audit_log.admin_user_id
      `;

      const [
        countRows
      ] = await pool.query(
        `
          SELECT
            COUNT(*) AS total_records

          ${joins}

          WHERE
            ${whereClause}
        `,
        parameters
      );

     const [
  auditRows
] = await pool.query(
  `
    SELECT
      audit_log.id,
      audit_log.admin_user_id,
      audit_log.action,
      audit_log.entity_type,
      audit_log.entity_id,
      audit_log.description,

      audit_log.metadata_json
        AS metadata,

      audit_log.ip_address,
      audit_log.user_agent,
      audit_log.created_at,

      admin_user.full_name
        AS admin_user_name,

      admin_user.username
        AS admin_username,

      admin_user.email
        AS admin_email,

      admin_user.role
        AS admin_user_role

    ${joins}

    WHERE
      ${whereClause}

    ORDER BY
      audit_log.created_at DESC,
      audit_log.id DESC

    LIMIT ?
    OFFSET ?
  `,
  [
    ...parameters,
    limit,
    offset
  ]
);

      const [
        overviewRows
      ] = await pool.query(
        `
          SELECT
            COUNT(*)
              AS total_events,

            SUM(
              CASE
                WHEN DATE(created_at) =
                  CURRENT_DATE
                  THEN 1

                ELSE 0
              END
            ) AS events_today,

            SUM(
              CASE
                WHEN created_at >=
                  DATE_SUB(
                    NOW(),
                    INTERVAL 7 DAY
                  )
                  THEN 1

                ELSE 0
              END
            ) AS events_last_7_days,

            SUM(
              CASE
                WHEN action LIKE
                  'ADMIN_USER_%'
                  THEN 1

                ELSE 0
              END
            ) AS account_events,

            COUNT(
              DISTINCT
              admin_user_id
            ) AS active_administrators

          FROM admin_audit_logs
        `
      );

      const [
        administratorRows
      ] = await pool.query(
        `
          SELECT DISTINCT
            admin_user.id,
            admin_user.full_name,
            admin_user.username,
            admin_user.email,
            admin_user.role,
            admin_user.status

          FROM admin_users
            AS admin_user

          INNER JOIN admin_audit_logs
            AS audit_log
            ON audit_log.admin_user_id =
              admin_user.id

          ORDER BY
            admin_user.full_name ASC
        `
      );

      const [
        actionRows
      ] = await pool.query(
        `
          SELECT DISTINCT
            action

          FROM admin_audit_logs

          WHERE
            action IS NOT NULL

            AND action <> ''

          ORDER BY
            action ASC
        `
      );

      const [
        entityTypeRows
      ] = await pool.query(
        `
          SELECT DISTINCT
            entity_type

          FROM admin_audit_logs

          WHERE
            entity_type IS NOT NULL

            AND entity_type <> ''

          ORDER BY
            entity_type ASC
        `
      );

      const totalRecords =
        toNumber(
          countRows[0]
            ?.total_records
        );

      const totalPages =
        totalRecords > 0
          ? Math.ceil(
              totalRecords /
              limit
            )
          : 1;

      return response.status(200).json({
        success: true,

        data: {
          logs:
            auditRows.map(
              (auditLog) => ({
                ...auditLog,

                id:
                  toNumber(
                    auditLog.id
                  ),

                admin_user_id:
                  auditLog.admin_user_id
                    ? toNumber(
                        auditLog
                          .admin_user_id
                      )
                    : null,

                entity_id:
                  auditLog.entity_id
                    ? toNumber(
                        auditLog
                          .entity_id
                      )
                    : null,

                metadata:
                  parseMetadata(
                    auditLog.metadata
                  )
              })
            ),

          overview: {
            total_events:
              toNumber(
                overviewRows[0]
                  ?.total_events
              ),

            events_today:
              toNumber(
                overviewRows[0]
                  ?.events_today
              ),

            events_last_7_days:
              toNumber(
                overviewRows[0]
                  ?.events_last_7_days
              ),

            account_events:
              toNumber(
                overviewRows[0]
                  ?.account_events
              ),

            active_administrators:
              toNumber(
                overviewRows[0]
                  ?.active_administrators
              )
          },

          filters: {
            search,
            action,

            entity_type:
              entityType,

            admin_user_id:
              adminUserId > 0
                ? adminUserId
                : null,

            date_from:
              dateFrom,

            date_to:
              dateTo
          },

          options: {
            administrators:
              administratorRows.map(
                (
                  administrator
                ) => ({
                  ...administrator,

                  id:
                    toNumber(
                      administrator.id
                    )
                })
              ),

            actions:
              actionRows.map(
                (row) =>
                  row.action
              ),

            entity_types:
              entityTypeRows.map(
                (row) =>
                  row.entity_type
              )
          },

          pagination: {
            current_page:
              page,

            per_page:
              limit,

            total_records:
              totalRecords,

            total_pages:
              totalPages,

            has_previous_page:
              page > 1,

            has_next_page:
              page <
              totalPages
          },

          permissions: {
            can_view:
              true,

            can_export:
              request.admin.role ===
              "super_admin"
          }
        }
      });
    } catch (error) {
      console.error(
        "Get administrator audit logs error:",
        error
      );

      return sendDevelopmentError(
        response,
        error,
        "Unable to load the administrator audit log."
      );
    }
  };

module.exports = {
  getAdminAuditLogs
};