// Runs once a day, to find users who have been inactive for 60 days and mark them
// as inactive by putting them into cold storage. This deletes the user's data from sendgrid
// as well to save on costs.

import { exit } from 'process';
import { emailSenderAPICall } from '../external-services/emailSender.js';
import { logger } from '../server-utils/logger.js';
import query from '../server-utils/query.js';
import { sendDiscordErrorMessage } from './notification.js';
import { insertJob } from './sendgridSyncFunctions.js';

interface User {
  id: number;
  email: string;
}

const BATCH_SIZE = 100; // This is 100 because sendgrid only allows us to lookup 100 contacts at a time
const getInactiveUsers = async (days: number = 250): Promise<User[]> => {
  // Gets users who have not had any activity in the last n days.
  // Ignores users who are already in cold storage.
  const result = await query(
    `
      WITH recent_activities AS (
        SELECT
          user_id,
          MAX(created_at) AS last_activity
        FROM activity
        WHERE created_at >= NOW() - INTERVAL '${days} days'
        GROUP BY user_id
      ),
      latest_cold_storage AS (
        SELECT DISTINCT ON (user_id) user_id, in_storage
        FROM user_email_cold_storage
        ORDER BY user_id, updated_at DESC
      )
      SELECT u.id, u.email
      FROM users u
      LEFT JOIN recent_activities ra ON u.id = ra.user_id
      LEFT JOIN latest_cold_storage cs ON u.id = cs.user_id
      WHERE ra.last_activity IS NULL AND (cs.user_id IS NULL OR cs.in_storage = false)
    `,
    []
  );
  return result.rows.map((row) => {
    return { id: row.id, email: row.email };
  });
};

const processInactiveUsers = async (days: number = 250, trial: boolean = true, limit: number = 0) => {
  const users = await getInactiveUsers(days);
  logger.info(`Found ${users.length} inactive users`);

  // Break up into batches of 4000
  const batchedUsers: User[][] = [];
  for (let i = 0; i < users.length; i += BATCH_SIZE) {
    batchedUsers.push(users.slice(i, i + BATCH_SIZE));
  }

  logger.info(`Processing ${batchedUsers.length} batches of ${BATCH_SIZE} users`);
  let processed = 0;
  for (const batch of batchedUsers) {
    logger.info(`Processing batch of ${batch.length} users. Total processed: ${processed}`);
    // Look for user on sendgrid

    const sendgridIDs: string[] = [];
    const processedUsers: string[] = [];

    try {
      const response = await emailSenderAPICall({
        method: 'POST',
        url: '/v3/marketing/contacts/search/emails',
        body: {
          emails: batch.map((u) => `${u.email}`),
        },
      });

      const contacts = response[0].body;

      for (const u of batch) {
        const contact = contacts['result'][u.email];
        if (contact && contact?.['contact']?.['id']) {
          // Safe access because if the contact is not found the object will be:
          // { 'email': { 'error': 'Contact not found' } }
          sendgridIDs.push(contact['contact']['id']);
          processedUsers.push(`${u.id}`);
          processed += 1;
          if (limit && limit > 0 && processed >= limit) {
            logger.info(`Limit of ${limit} reached, stopping`);
            break;
          }
        }
      }
    } catch (e) {
      logger.error('Failed to search for users on sendgrid');
      continue;
    }

    logger.info(`Found ${sendgridIDs.length} contacts on sendgrid`);

    // Delete users from sendgrid
    if (!trial) {
      if (sendgridIDs.length === 0) {
        logger.info('No users to delete');
        continue;
      }
      const deleteResponse = await emailSenderAPICall({
        method: 'DELETE',
        url: '/v3/marketing/contacts',
        qs: {
          ids: sendgridIDs.join(','),
        },
      });
      const jobId = deleteResponse[0].body['job_id'];

      logger.info('Job ID: ', jobId);

      if (!jobId) {
        await sendDiscordErrorMessage(
          `\`longInactiveUsers\` error.\nFailed to delete users.\nResponse:${JSON.stringify(deleteResponse)}`
        );
        logger.error('Failed to delete users from sendgrid');
        return;
      } else {
        insertJob(jobId, 'longInactiveUsers.ts');
      }

      // Mark users as inactive
      await query(
        `
        WITH Difference_CTE AS (
          SELECT id from users WHERE id = ANY($1::integer[])
          EXCEPT
          SELECT user_id FROM user_email_cold_storage WHERE in_storage = TRUE
        )
        INSERT INTO user_email_cold_storage (user_id)
        SELECT id FROM Difference_CTE;
      `,
        [processedUsers]
      );
    } else {
      logger.info('Trial run, not deleting users');
    }

    if (limit && limit > 0 && processed >= limit) {
      logger.info(`Limit of ${limit} reached, stopping outer loop`);
      break;
    }
  }
};

const args = process.argv.slice(2);
let params = {};

for (let i = 0; i < args.length; i++) {
  let arg = args[i];
  if (arg.startsWith('--')) {
    let [key, value] = arg.slice(2).split('=');
    params[key] = value;
  }
}

const main = async () => {
  const defaults = {
    days: '250',
    trial: 'true',
    limit: '0',
  };
  const { days, trial, limit } = { ...defaults, ...params };
  logger.info(`Running with days=${days}, trial=${trial}, limit=${limit}`);
  await processInactiveUsers(parseInt(days), trial === 'true', parseInt(limit));
  exit(0);
};

main();
