const { getJobAssignmentType, formatTaskPriority } = require("./stringHelpers");
const moment = require("moment-timezone");
const { startCase } = require("lodash");
const Dinero = require("dinero.js");

Dinero.globalRoundingMode = "HALF_EVEN";

/**
 * Creates a standard notification object with app, email, and FCM properties and populates recipients.
 * @param {Object} data - Input data containing jobId, jobRoNumber, bodyShopId, bodyShopName, and scenarioWatchers
 * @param {string} key - Notification key for the app
 * @param {string} body - Notification body text
 * @param {Object} [variables={}] - Variables for the app notification
 * @returns {Object} Notification object with populated recipients
 */
const buildNotification = (data, key, body, variables = {}) => {
  const result = {
    app: {
      jobId: data.jobId,
      jobRoNumber: data.jobRoNumber,
      bodyShopId: data.bodyShopId,
      scenarioKey: data.scenarioKey,
      scenarioTable: data.scenarioTable,
      key,
      body,
      variables,
      recipients: []
    },
    email: {
      jobId: data.jobId,
      jobRoNumber: data.jobRoNumber,
      bodyShopName: data.bodyShopName,
      bodyShopTimezone: data.bodyShopTimezone,
      body,
      recipients: []
    },
    fcm: {
      jobId: data.jobId,
      jobRoNumber: data.jobRoNumber,
      bodyShopId: data.bodyShopId,
      bodyShopName: data.bodyShopName,
      bodyShopTimezone: data.bodyShopTimezone,
      scenarioKey: data.scenarioKey,
      scenarioTable: data.scenarioTable,
      key,
      body,
      variables,
      recipients: []
    }
  };

  // Populate recipients from scenarioWatchers
  data.scenarioWatchers.forEach((recipients) => {
    const { user, app, fcm, email, firstName, lastName, employeeId, associationId } = recipients;

    if (app === true) {
      result.app.recipients.push({
        user,
        bodyShopId: data.bodyShopId,
        employeeId,
        associationId
      });
    }

    if (email === true) {
      result.email.recipients.push({ user, firstName, lastName });
    }

    if (fcm === true) {
      // Keep structure consistent and future-proof (token lookup is done server-side)
      result.fcm.recipients.push({
        user,
        bodyShopId: data.bodyShopId,
        employeeId,
        associationId
      });
    }
  });

  return result;
};

/**
 * Creates a notification for when the alternate transport is changed.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const alternateTransportChangedBuilder = (data) => {
  const oldTransport = data?.changedFields?.alt_transport?.old;
  const newTransport = data?.changedFields?.alt_transport?.new;
  let body;

  if (oldTransport && newTransport)
    body = `The alternate transportation has been changed from ${oldTransport} to ${newTransport}.`;
  else if (!oldTransport && newTransport) body = `The alternate transportation has been set to ${newTransport}.`;
  else if (oldTransport && !newTransport)
    body = `The alternate transportation has been canceled (previously ${oldTransport}).`;
  else body = `The alternate transportation has been updated.`;

  return buildNotification(data, "notifications.job.alternateTransportChanged", body, {
    alternateTransport: newTransport,
    oldAlternateTransport: oldTransport
  });
};

/**
 * Creates a notification for when a bill is posted.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const billPostedBuilder = (data) => {
  const facing = data?.data?.isinhouse ? "An In House" : "A Vendor";
  const body = `${facing} ${data?.data?.is_credit_memo ? "credit memo" : "bill"} has been posted.`.trim();

  return buildNotification(data, "notifications.job.billPosted", body, {
    isInHouse: data?.data?.isinhouse,
    isCreditMemo: data?.data?.is_credit_memo
  });
};

/**
 * Creates a notification for when the status of critical parts changes.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const criticalPartsStatusChangedBuilder = (data) => {
  const lineDesc = data?.data?.line_desc;
  const status = data?.data?.status;
  const body = status
    ? `The status on a critical part line (${lineDesc}) has been set to ${status}.`
    : `The status on a critical part line (${lineDesc}) has been cleared.`;

  return buildNotification(data, "notifications.job.criticalPartsStatusChanged", body, {
    joblineId: data?.data?.id,
    status: data?.data?.status,
    line_desc: lineDesc
  });
};

/**
 * Creates a notification for when the intake or delivery checklist is completed.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const intakeDeliveryChecklistCompletedBuilder = (data) => {
  const checklistType = data?.changedFields?.intakechecklist ? "intake" : "delivery";
  const body = `The ${checklistType.charAt(0).toUpperCase() + checklistType.slice(1)} checklist has been completed.`;

  return buildNotification(data, "notifications.job.checklistCompleted", body, {
    checklistType,
    completed: true
  });
};

/**
 * Creates a notification for when a job is assigned to the user.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const jobAssignedToMeBuilder = (data) => {
  const body = `You have been assigned to ${getJobAssignmentType(data.scenarioFields?.[0])}.`;

  return buildNotification(data, "notifications.job.assigned", body, {
    type: data.scenarioFields?.[0]
  });
};

/**
 * Creates a notification for when jobs are added to production.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const jobsAddedToProductionBuilder = (data) => {
  const body = `Job is now in production.`;
  return buildNotification(data, "notifications.job.addedToProduction", body);
};

/**
 * Creates a notification for when the job status changes.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const jobStatusChangeBuilder = (data) => {
  const oldStatus = data?.changedFields?.status?.old;
  const newStatus = data?.changedFields?.status?.new;
  let body;

  if (oldStatus && newStatus) body = `The status has been changed from ${oldStatus} to ${newStatus}.`;
  else if (!oldStatus && newStatus) body = `The status has been set to ${newStatus}.`;
  else if (oldStatus && !newStatus) body = `The status has been cleared (previously ${oldStatus}).`;
  else body = `The status has been updated.`;

  return buildNotification(data, "notifications.job.statusChanged", body, {
    status: newStatus,
    oldStatus: oldStatus
  });
};

/**
 * Creates a notification for when new media is added or reassigned.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const newMediaAddedReassignedBuilder = (data) => {
  const mediaType = data?.data?.type?.startsWith("image") ? "Image" : "Document";
  const action = data?.data?._documentMoved
    ? "moved to another job"
    : data.isNew
      ? "added"
      : data.changedFields?.jobid && data.changedFields.jobid.old !== data.changedFields.jobid.new
        ? "moved to this job"
        : "updated";
  const body = `A ${mediaType} has been ${action}.`;

  return buildNotification(data, "notifications.job.newMediaAdded", body, {
    mediaType,
    action,
    movedToJob: data?.data?._movedToJob
  });
};

/**
 * Creates a notification for when a new note is added.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const newNoteAddedBuilder = (data) => {
  const body = [
    "A",
    data?.data?.critical && "critical",
    data?.data?.private && "private",
    data?.data?.type,
    "note has been added by",
    `${data.data.created_by}`
  ]
    .filter(Boolean)
    .join(" ");

  return buildNotification(data, "notifications.job.newNoteAdded", body, {
    createdBy: data?.data?.created_by,
    critical: data?.data?.critical,
    type: data?.data?.type,
    private: data?.data?.private
  });
};

/**
 * Creates a notification for when a new time ticket is posted.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const newTimeTicketPostedBuilder = (data) => {
  const type = data?.data?.cost_center;
  const body = `A ${startCase(type.toLowerCase())} time ticket for ${data?.data?.date} has been posted.`.trim();

  return buildNotification(data, "notifications.job.newTimeTicketPosted", body, {
    type,
    date: data?.data?.date
  });
};

/**
 * Creates a notification for when a part is marked as back-ordered.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const partMarkedBackOrderedBuilder = (data) => {
  const body = `A part ${data?.data?.line_desc} has been marked as back-ordered.`;

  return buildNotification(data, "notifications.job.partBackOrdered", body, {
    line_desc: data?.data?.line_desc
  });
};

/**
 * Creates a notification for when payment is collected or completed.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const paymentCollectedCompletedBuilder = (data) => {
  const momentFormat = "MM/DD/YYYY";
  const amountDinero = Dinero({ amount: Math.round((data.data.amount || 0) * 100) });
  const amountFormatted = amountDinero.toFormat();
  const payer = data.data.payer;
  const paymentType = data.data.type;
  const paymentDate = moment(data.data.date).format(momentFormat);
  const body = `Payment of ${amountFormatted} has been collected from ${payer} via ${paymentType} on ${paymentDate}`;

  return buildNotification(data, "notifications.job.paymentCollected", body, {
    amount: data.data.amount,
    payer: data.data.payer,
    type: data.data.type,
    date: data.data.date
  });
};

/**
 * Creates a notification for when scheduled dates are changed.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const scheduledDatesChangedBuilder = (data) => {
  const changedFields = data.changedFields;
  const fieldConfigs = {
    scheduled_in: "Scheduled In",
    scheduled_completion: "Scheduled Completion",
    scheduled_delivery: "Scheduled Delivery"
  };
  const formatDateTime = (date) => {
    if (!date) return "(no date set)";
    const formatted = moment(date).tz(data.bodyShopTimezone);
    return `${formatted.format("MM/DD/YYYY")} at ${formatted.format("hh:mm a")}`;
  };

  const fieldMessages = Object.entries(fieldConfigs)
    .filter(([field]) => changedFields[field])
    .map(([field, label]) => {
      const { old, new: newValue } = changedFields[field];
      if (old && !newValue) return `${label} was cancelled (previously ${formatDateTime(old)}).`;
      else if (!old && newValue) return `${label} was set to ${formatDateTime(newValue)}.`;
      else if (old && newValue) return `${label} changed from ${formatDateTime(old)} to ${formatDateTime(newValue)}.`;
      return "";
    })
    .filter(Boolean);

  const body = fieldMessages.length > 0 ? fieldMessages.join(" ") : "Scheduled dates have been updated.";

  return buildNotification(data, "notifications.job.scheduledDatesChanged", body, {
    scheduledIn: changedFields.scheduled_in?.new,
    oldScheduledIn: changedFields.scheduled_in?.old,
    scheduledCompletion: changedFields.scheduled_completion?.new,
    oldScheduledCompletion: changedFields.scheduled_completion?.old,
    scheduledDelivery: changedFields.scheduled_delivery?.new,
    oldScheduledDelivery: changedFields.scheduled_delivery?.old
  });
};

/**
 * Creates a notification for when tasks are updated or created.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const tasksUpdatedCreatedBuilder = (data) => {
  const momentFormat = "MM/DD/YYYY hh:mm a";
  const timezone = data.bodyShopTimezone;
  const taskTitle = data?.data?.title ? `"${data.data.title}"` : "Unnamed Task";

  let body, variables;
  if (data.isNew) {
    const priority = formatTaskPriority(data?.data?.priority);
    const createdBy = data?.data?.created_by || "Unknown";
    const dueDate = data.data.due_date ? ` due on ${moment(data.data.due_date).tz(timezone).format(momentFormat)}` : "";
    const completedOnCreation = data.data.completed === true;
    body = `A ${priority} task ${taskTitle} has been created${completedOnCreation ? " and marked completed" : ""} by ${createdBy}${dueDate}.`;
    variables = {
      isNew: data.isNew,
      roNumber: data.jobRoNumber,
      title: data?.data?.title,
      priority: data?.data?.priority,
      createdBy: data?.data?.created_by,
      dueDate: data?.data?.due_date,
      completed: completedOnCreation ? data?.data?.completed : undefined
    };
  } else {
    const changedFields = data.changedFields;
    const fieldNames = Object.keys(changedFields);
    const oldTitle = changedFields.title ? `"${changedFields.title.old || "Unnamed Task"}"` : taskTitle;

    if (fieldNames.length === 1 && changedFields.completed) {
      body = `Task ${oldTitle} was marked ${changedFields.completed.new ? "complete" : "incomplete"}`;
      variables = {
        isNew: data.isNew,
        roNumber: data.jobRoNumber,
        title: data?.data?.title,
        changedCompleted: changedFields.completed.new
      };
    } else {
      const fieldMessages = [];
      if (changedFields.title)
        fieldMessages.push(`Task ${oldTitle} changed title to "${changedFields.title.new || "unnamed task"}".`);
      if (changedFields.description) fieldMessages.push("Description updated.");
      if (changedFields.priority)
        fieldMessages.push(`Priority changed to ${formatTaskPriority(changedFields.priority.new)}.`);
      if (changedFields.due_date)
        fieldMessages.push(`Due date set to ${moment(changedFields.due_date.new).tz(timezone).format(momentFormat)}.`);
      if (changedFields.completed)
        fieldMessages.push(`Status changed to ${changedFields.completed.new ? "complete" : "incomplete"}.`);

      body =
        fieldMessages.length > 0
          ? fieldMessages.length === 1 && changedFields.title
            ? fieldMessages[0]
            : `Task ${oldTitle} updated: ${fieldMessages.join(", ")}`
          : `Task ${oldTitle} has been updated.`;
      variables = {
        isNew: data.isNew,
        roNumber: data.jobRoNumber,
        title: data?.data?.title,
        changedTitleOld: changedFields.title?.old,
        changedTitleNew: changedFields.title?.new,
        changedPriority: changedFields.priority?.new,
        changedDueDate: changedFields.due_date?.new,
        changedCompleted: changedFields.completed?.new
      };
    }
  }

  return buildNotification(
    data,
    data.isNew ? "notifications.job.taskCreated" : "notifications.job.taskUpdated",
    body,
    variables
  );
};

/**
 * Creates a notification for when a supplement is imported.
 * @param data
 * @returns {{app: {jobId, jobRoNumber: *, bodyShopId: *, key: string, body: string, variables: Object, recipients: *[]}, email: {jobId, jobRoNumber: *, bodyShopName: *, body: string, recipients: *[]}, fcm: {recipients: *[]}}}
 */
const supplementImportedBuilder = (data) => {
  const body = `A supplement has been imported.`;
  return buildNotification(data, "notifications.job.supplementImported", body);
};

module.exports = {
  alternateTransportChangedBuilder,
  billPostedBuilder,
  criticalPartsStatusChangedBuilder,
  intakeDeliveryChecklistCompletedBuilder,
  jobAssignedToMeBuilder,
  jobsAddedToProductionBuilder,
  jobStatusChangeBuilder,
  newMediaAddedReassignedBuilder,
  newNoteAddedBuilder,
  newTimeTicketPostedBuilder,
  partMarkedBackOrderedBuilder,
  paymentCollectedCompletedBuilder,
  scheduledDatesChangedBuilder,
  supplementImportedBuilder,
  tasksUpdatedCreatedBuilder
};
