/**
 * CONFIGURATION BLOCK
 * -------------------
 * Update these values before running `npm run push` (clasp push) to deploy
 * the script to your Google Workspace environment.
 */
const CONFIG = {
  // If the script is bound to a sheet (using `clasp clone <id>`), you can leave this empty.
  // If deployed as a standalone script, YOU MUST provide the exact Spreadsheet ID.
  SPREADSHEET_ID: "1q4r08nBA_ClWv3ypPCQ6GVCfMVkQwSKzDSRiokkQQ8Q",

  // The exact name of the tab containing the data (e.g., from Google Forms)
  SHEET_NAME: "Form Responses 1",

  // Column indices (1-indexed). 
  // Example: If Source is Column B, index is 2. If Dest is Column C, index is 3.
  COL_SOURCE_ADDRESS: 2,
  COL_DESTINATION_ADDRESS: 6,

  // The admin email that should receive the execution reports
  ADMIN_EMAIL: "moritz@haumdaucher.de",

  // The primary Workspace domain for validation and auto-appending
  WORKSPACE_DOMAIN: "haumdaucher.de",

  // Dry run mode. If true, the script will only log what it would do and send the email,
  // but will NOT actually create, update, or delete any groups in Workspace.
  DRY_RUN: true,

  // DO NOT CHANGE THIS VALUE.
  // This tag is added to the description of groups created by this script.
  // It acts as a safety guard to ensure the script NEVER deletes manually created groups.
  GROUP_DESCRIPTION_TAG: "[Auto-Forwarder] Managed by Google Sheets"
};

/**
 * INSTALLATION:
 * Run this function ONCE manually from the Apps Script IDE after pushing.
 * It authorizes the script and installs the background trigger.
 */
function setup() {
  const ss = CONFIG.SPREADSHEET_ID
    ? SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID)
    : SpreadsheetApp.getActiveSpreadsheet();

  if (!ss) {
    throw new Error("Could not find spreadsheet. Ensure you are bound to a sheet or have set CONFIG.SPREADSHEET_ID.");
  }

  // Clean up any existing triggers to prevent duplicates
  const triggers = ScriptApp.getProjectTriggers();
  for (let i = 0; i < triggers.length; i++) {
    if (triggers[i].getHandlerFunction() === 'syncForwardings') {
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }

  // Install the onFormSubmit trigger (specifically for Google Forms submissions)
  ScriptApp.newTrigger('syncForwardings')
    .forSpreadsheet(ss)
    .onFormSubmit()
    .create();

  // Also install an onChange trigger as a fallback (for manual sheet edits)
  ScriptApp.newTrigger('syncForwardings')
    .forSpreadsheet(ss)
    .onChange()
    .create();

  console.log("Setup complete. The 'syncForwardings' trigger has been installed.");
}

/**
 * MAIN ENTRY POINT
 * This is triggered automatically whenever the spreadsheet changes (e.g., new form submission).
 */
function syncForwardings() {
  try {
    console.log("Starting syncForwardings...");
    if (CONFIG.DRY_RUN) {
      console.log("dry_run enabled, no changes will be applied.");
    } else {
      console.log("dry_run is false, actively performing changes.");
    }

    const desiredState = readDesiredStateFromSheet();
    const currentState = readCurrentStateFromWorkspace();

    const changelog = [];

    // 1. Process Creations and Updates
    for (const sourceEmail in desiredState) {
      const desiredDestinations = desiredState[sourceEmail];

      if (!currentState[sourceEmail]) {
        createForwardingGroup(sourceEmail, desiredDestinations, changelog);
      } else {
        updateForwardingGroup(sourceEmail, currentState[sourceEmail], desiredDestinations, changelog);
      }
    }

    // 2. Process Deletions
    for (const groupEmail in currentState) {
      if (!desiredState[groupEmail]) {
        deleteForwardingGroup(groupEmail, changelog);
      }
    }

    // 3. Send Admin Report
    sendReport(desiredState, changelog);

    console.log("Sync complete.");
  } catch (err) {
    console.error("Fatal error during syncForwardings:", err);
    MailApp.sendEmail({
      to: CONFIG.ADMIN_EMAIL,
      subject: "[ERROR] Mail Forwarding Sync Failed",
      body: "The mail forwarding automation encountered a fatal error:\n\n" + err.stack + "\n\nMessage: " + err.message
    });
    // Re-throw the error so Apps Script dashboard shows "Failed"
    throw err;
  }
}

/**
 * Reads the Google Sheet and builds a map of Source -> [Destinations]
 */
function readDesiredStateFromSheet() {
  const ss = CONFIG.SPREADSHEET_ID
    ? SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID)
    : SpreadsheetApp.getActiveSpreadsheet();

  const sheet = ss.getSheetByName(CONFIG.SHEET_NAME);
  if (!sheet) {
    throw new Error(`Sheet '${CONFIG.SHEET_NAME}' not found.`);
  }

  const data = sheet.getDataRange().getValues();
  const desiredState = {};

  // Regex for basic email validation
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

  // Start at i=1 to skip the header row
  for (let i = 1; i < data.length; i++) {
    const row = data[i];
    let source = (row[CONFIG.COL_SOURCE_ADDRESS - 1] || "").toString().trim().toLowerCase();
    let dest = (row[CONFIG.COL_DESTINATION_ADDRESS - 1] || "").toString().trim().toLowerCase();

    // Ignore completely empty rows
    if (!source && !dest) {
      continue;
    }

    // 1. Auto-append domain to source if they just typed a name (e.g. "frederic")
    if (source && !source.includes("@")) {
      source += `@${CONFIG.WORKSPACE_DOMAIN}`;
    }

    // 2. Validate source domain
    if (source && !source.endsWith(`@${CONFIG.WORKSPACE_DOMAIN}`)) {
      console.warn(`Row ${i + 1}: Skipped. Source address must belong to @${CONFIG.WORKSPACE_DOMAIN} domain. Found: '${source}'`);
      continue;
    }

    // 3. Validate source is a valid email
    if (source && !emailRegex.test(source)) {
      console.warn(`Row ${i + 1}: Skipped. Invalid source email format: '${source}'`);
      continue;
    }

    // 4. Validate destination is a valid email
    if (dest && !emailRegex.test(dest)) {
      console.warn(`Row ${i + 1}: Skipped. Invalid destination email format: '${dest}'`);
      continue;
    }

    if (source && dest) {
      console.log(`Parsed Row ${i + 1}: Valid mapping [${source} -> ${dest}]`);
      if (!desiredState[source]) {
        desiredState[source] = [];
      }
      // Ensure we don't add duplicate destinations for the same source
      if (desiredState[source].indexOf(dest) === -1) {
        desiredState[source].push(dest);
      }
    } else {
      console.warn(`Row ${i + 1}: Skipped. Missing either source or destination (Source: '${source}', Dest: '${dest}')`);
    }
  }

  return desiredState;
}

/**
 * Queries the Admin Directory API for existing Groups managed by this script
 */
function readCurrentStateFromWorkspace() {
  const currentState = {};

  // customer: 'my_customer' is a special alias for the primary domain
  let pageToken;
  do {
    const response = AdminDirectory.Groups.list({
      customer: 'my_customer',
      pageToken: pageToken
    });

    const groups = response.groups || [];
    for (const group of groups) {
      // ONLY process groups that have our specific safety tag
      if (group.description && group.description.indexOf(CONFIG.GROUP_DESCRIPTION_TAG) !== -1) {
        const email = group.email.toLowerCase();
        currentState[email] = getGroupMembers(email);
      }
    }

    pageToken = response.nextPageToken;
  } while (pageToken);

  return currentState;
}

/**
 * Fetches all members for a specific group
 */
function getGroupMembers(groupEmail) {
  const members = [];
  let pageToken;

  do {
    const response = AdminDirectory.Members.list(groupEmail, {
      pageToken: pageToken
    });

    const memberList = response.members || [];
    for (const member of memberList) {
      if (member.email) {
        members.push(member.email.toLowerCase());
      }
    }

    pageToken = response.nextPageToken;
  } while (pageToken);

  return members;
}

function logAction(changelog, message) {
  console.log(message);
  changelog.push(message);
}

/**
 * Creates a new Workspace Group to act as a forwarder
 */
function createForwardingGroup(sourceEmail, destinations, changelog) {
  if (CONFIG.DRY_RUN) {
    logAction(changelog, `[DRY RUN - CREATED] Group: ${sourceEmail}`);
    for (const dest of destinations) {
      logAction(changelog, `  + [DRY RUN] Added member: ${dest}`);
    }
    return;
  }

  try {
    const newGroup = {
      email: sourceEmail,
      name: `Auto-Forwarder: ${sourceEmail}`,
      description: CONFIG.GROUP_DESCRIPTION_TAG
    };

    AdminDirectory.Groups.insert(newGroup);
    logAction(changelog, `[CREATED] Group: ${sourceEmail}`);

    for (const dest of destinations) {
      AdminDirectory.Members.insert({ email: dest, role: 'MEMBER' }, sourceEmail);
      logAction(changelog, `  + Added member: ${dest}`);
    }
  } catch (e) {
    logAction(changelog, `[ERROR] Failed to create group ${sourceEmail}: ${e.message}`);
  }
}

/**
 * Reconciles members of an existing forwarding group
 */
function updateForwardingGroup(sourceEmail, currentMembers, desiredMembers, changelog) {
  // 1. Add missing members
  for (const dest of desiredMembers) {
    if (currentMembers.indexOf(dest) === -1) {
      if (CONFIG.DRY_RUN) {
        logAction(changelog, `[DRY RUN - UPDATED] Group ${sourceEmail}: Added member ${dest}`);
      } else {
        try {
          AdminDirectory.Members.insert({ email: dest, role: 'MEMBER' }, sourceEmail);
          logAction(changelog, `[UPDATED] Group ${sourceEmail}: Added member ${dest}`);
        } catch (e) {
          logAction(changelog, `[ERROR] Failed to add member ${dest} to ${sourceEmail}: ${e.message}`);
        }
      }
    }
  }

  // 2. Remove obsolete members
  for (const current of currentMembers) {
    if (desiredMembers.indexOf(current) === -1) {
      if (CONFIG.DRY_RUN) {
        logAction(changelog, `[DRY RUN - UPDATED] Group ${sourceEmail}: Removed member ${current}`);
      } else {
        try {
          AdminDirectory.Members.remove(sourceEmail, current);
          logAction(changelog, `[UPDATED] Group ${sourceEmail}: Removed member ${current}`);
        } catch (e) {
          logAction(changelog, `[ERROR] Failed to remove member ${current} from ${sourceEmail}: ${e.message}`);
        }
      }
    }
  }
}

/**
 * Deletes a forwarding group (Requires the safety tag check performed during read)
 */
function deleteForwardingGroup(groupEmail, changelog) {
  if (CONFIG.DRY_RUN) {
    logAction(changelog, `[DRY RUN - DELETED] Group: ${groupEmail}`);
    return;
  }

  try {
    AdminDirectory.Groups.remove(groupEmail);
    logAction(changelog, `[DELETED] Group: ${groupEmail}`);
  } catch (e) {
    logAction(changelog, `[ERROR] Failed to delete group ${groupEmail}: ${e.message}`);
  }
}

/**
 * Sends a summary report to the configured Admin Email
 */
function sendReport(desiredState, changelog) {
  if (changelog.length === 0) {
    console.log("No changes detected. Skipping email report.");
    return;
  }

  const subject = "[Haumdaucher] Mail Forwarding Sync Report";

  let body = "The mail forwarding automation has executed successfully.\n\n";

  body += "=== CHANGELOG ===\n";
  body += changelog.join("\n") + "\n\n";

  body += "=== ACTIVE FORWARDINGS ===\n";
  const sources = Object.keys(desiredState).sort();
  if (sources.length === 0) {
    body += "No active forwardings configured in the sheet.\n";
  } else {
    for (const source of sources) {
      body += `${source} -> ${desiredState[source].join(", ")}\n`;
    }
  }

  MailApp.sendEmail({
    to: CONFIG.ADMIN_EMAIL,
    subject: subject,
    body: body
  });
}
