/**
 * @module autoAddWatchers
 * @description
 * This module handles automatically adding watchers to new jobs based on the notifications_autoadd
 * boolean field in the associations table and the notification_followers JSON field in the bodyshops table.
 * It ensures users are not added twice and logs the process.
 *
 * NOTE: Bodyshop notification_followers is fetched directly from the DB (Hasura) to avoid stale Redis cache.
 */

const { client: gqlClient } = require("../graphql-client/graphql-client");
const { isEmpty } = require("lodash");
const {
  GET_BODYSHOP_WATCHERS_BY_ID,
  GET_JOB_WATCHERS_MINIMAL,
  GET_NOTIFICATION_WATCHERS,
  INSERT_JOB_WATCHERS
} = require("../graphql-client/queries");

// If true, the user who commits the action will NOT receive notifications; if false, they will.
const FILTER_SELF_FROM_WATCHERS = process.env?.FILTER_SELF_FROM_WATCHERS !== "false";

/**
 * Adds watchers to a new job based on notifications_autoadd and notification_followers.
 *
 * @param {Object} req - The request object containing event data and logger.
 * @returns {Promise<void>} Resolves when watchers are added or if no action is needed.
 * @throws {Error} If critical data (e.g., jobId, shopId) is missing.
 */
const autoAddWatchers = async (req) => {
  const { event, trigger } = req.body;
  const { logger } = req;

  // Validate that this is an INSERT event, bail
  if (trigger?.name !== "notifications_jobs_autoadd" || event.op !== "INSERT" || event.data.old) {
    return;
  }

  const jobId = event?.data?.new?.id;
  const shopId = event?.data?.new?.shopid;
  const roNumber = event?.data?.new?.ro_number || "unknown";
  const createdUserEmail = event?.data?.new?.created_user_email || "Unknown";

  if (!jobId || !shopId) {
    throw new Error(`Missing jobId (${jobId}) or shopId (${shopId}) for auto-add watchers`);
  }

  const hasuraUserRole = event?.session_variables?.["x-hasura-role"];
  const hasuraUserId = event?.session_variables?.["x-hasura-user-id"];

  try {
    // Fetch bodyshop data directly from DB (avoid Redis staleness)
    const bodyshopResponse = await gqlClient.request(GET_BODYSHOP_WATCHERS_BY_ID, { id: shopId });
    const bodyshopData = bodyshopResponse?.bodyshops_by_pk;

    const notificationFollowersRaw = bodyshopData?.notification_followers;
    const notificationFollowers = Array.isArray(notificationFollowersRaw)
      ? [...new Set(notificationFollowersRaw.filter((id) => id))] // de-dupe + remove falsy
      : [];

    // Execute queries in parallel
    const [notificationData, existingWatchersData] = await Promise.all([
      gqlClient.request(GET_NOTIFICATION_WATCHERS, {
        shopId,
        employeeIds: notificationFollowers,
        createdUserEmail
      }),
      gqlClient.request(GET_JOB_WATCHERS_MINIMAL, { jobid: jobId })
    ]);

    // Get users with notifications_autoadd: true
    const autoAddUsers =
      notificationData?.associations?.map((assoc) => ({
        email: assoc.useremail,
        associationId: assoc.id
      })) || [];

    // Get users from notification_followers (employee IDs -> employee emails)
    const followerEmails =
      notificationData?.employees
        ?.filter((e) => e.user_email)
        ?.map((e) => ({
          email: e.user_email,
          associationId: null
        })) || [];

    // Combine and deduplicate emails (use email as the unique key)
    const usersToAdd = [...autoAddUsers, ...followerEmails].reduce((acc, user) => {
      if (user?.email && !acc.some((u) => u.email === user.email)) {
        acc.push(user);
      }
      return acc;
    }, []);

    if (isEmpty(usersToAdd)) {
      return;
    }

    // Check existing watchers to avoid duplicates
    const existingWatcherEmails = existingWatchersData?.job_watchers?.map((w) => w.user_email) || [];

    // Filter out already existing watchers and optionally the user who created the job
    const newWatchers = usersToAdd
      .filter((user) => !existingWatcherEmails.includes(user.email))
      .filter((user) => {
        if (FILTER_SELF_FROM_WATCHERS && hasuraUserRole === "user") {
          const userData = existingWatchersData?.job_watchers?.find((w) => w.user?.authid === hasuraUserId);
          return userData ? user.email !== userData.user_email : true;
        }
        return true;
      })
      .map((user) => ({
        jobid: jobId,
        user_email: user.email
      }));

    if (isEmpty(newWatchers)) {
      return;
    }

    // Insert new watchers
    await gqlClient.request(INSERT_JOB_WATCHERS, { watchers: newWatchers });
  } catch (error) {
    logger.log("Error adding auto-add watchers", "error", "notifications", null, {
      message: error?.message,
      stack: error?.stack,
      jobId,
      shopId,
      roNumber
    });
    throw error; // Re-throw to ensure the error is logged in the handler
  }
};

module.exports = { autoAddWatchers };
