/**
 * 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 Forward Address is Column D, index is 4. If Forward To is Column F, index is 6.
  COL_FORWARD_ADDRESS: 6,
  COL_FORWARD_TO_ADDRESS: 4,

  // 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: false,

  // 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();

  // Verify and log the installed triggers so we can confirm the setup is correct
  const installedTriggers = ScriptApp.getProjectTriggers();
  console.log(`Setup complete. ${installedTriggers.length} trigger(s) are now active:`);
  for (const trigger of installedTriggers) {
    console.log(`  - Function: ${trigger.getHandlerFunction()}, Event: ${trigger.getEventType()}, Source: ${trigger.getTriggerSource()}`);
  }
}

/**
 * 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 forwardAddress in desiredState) {
      const forwardToAddresses = desiredState[forwardAddress];

      if (!currentState[forwardAddress]) {
        createForwardingGroup(forwardAddress, forwardToAddresses, changelog);
      } else {
        updateForwardingGroup(forwardAddress, currentState[forwardAddress], forwardToAddresses, 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 forwardAddress = (row[CONFIG.COL_FORWARD_ADDRESS - 1] || "").toString().trim().toLowerCase();
    let forwardToAddress = (row[CONFIG.COL_FORWARD_TO_ADDRESS - 1] || "").toString().trim().toLowerCase();

    // Ignore completely empty rows
    if (!forwardAddress && !forwardToAddress) {
      continue;
    }

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

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

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

    // 4. Validate forwardToAddress domain (MUST NOT be internal)
    if (forwardToAddress && forwardToAddress.endsWith(`@${CONFIG.WORKSPACE_DOMAIN}`)) {
      console.warn(`Row ${i + 1}: Skipped. Destination address cannot be an internal @${CONFIG.WORKSPACE_DOMAIN} address. Found: '${forwardToAddress}'`);
      continue;
    }

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

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

  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(forwardAddress, forwardToAddresses, changelog) {
  if (CONFIG.DRY_RUN) {
    logAction(changelog, `[DRY RUN - CREATED] Group: ${forwardAddress}`);
    for (const forwardTo of forwardToAddresses) {
      logAction(changelog, `  + [DRY RUN] Added member: ${forwardTo}`);
    }
    return;
  }

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

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

    // Propagation delay: Google Workspace APIs are eventually consistent.
    // We must wait a few seconds after creation before we can add members to the new group.
    Utilities.sleep(3000);

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

/**
 * Reconciles members of an existing forwarding group
 */
function updateForwardingGroup(forwardAddress, currentMembers, desiredMembers, changelog) {
  // 1. Add missing members
  for (const forwardTo of desiredMembers) {
    if (currentMembers.indexOf(forwardTo) === -1) {
      if (CONFIG.DRY_RUN) {
        logAction(changelog, `[DRY RUN - UPDATED] Group ${forwardAddress}: Added member ${forwardTo}`);
      } else {
        try {
          AdminDirectory.Members.insert({ email: forwardTo, role: 'MEMBER' }, forwardAddress);
          logAction(changelog, `[UPDATED] Group ${forwardAddress}: Added member ${forwardTo}`);
        } catch (e) {
          logAction(changelog, `[ERROR] Failed to add member ${forwardTo} to ${forwardAddress}: ${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 ${forwardAddress}: Removed member ${current}`);
      } else {
        try {
          AdminDirectory.Members.remove(forwardAddress, current);
          logAction(changelog, `[UPDATED] Group ${forwardAddress}: Removed member ${current}`);
        } catch (e) {
          logAction(changelog, `[ERROR] Failed to remove member ${current} from ${forwardAddress}: ${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
  });
}
