import { MailDataRequired } from '@sendgrid/mail';
import bodyParser from 'body-parser';
import dotenv from 'dotenv';
import { logger } from '../server-utils/logger.js';
import query from '../server-utils/query.js';
import { declareHandler } from '../server-utils/routesHandler.js';
import { sendIfAllRecipientsEmailable } from './emailSender.js';
import Sentry from './sentry.js';

export const SG_TEMPLATE_KEITH_WELCOME = 'd-9652523a92d6468a950458ae73a20435';
export const SG_TEMPLATE_SAM_WELCOME = 'd-7b71a11e47ec4c2cbef556290961c2bf';
export const SG_TEMPLATE_EMILEA_WELCOME = 'd-18691a0b2e8547aeb8822cdd6597ffac';
export const SG_TEMPLATE_RYAN_WELCOME = 'd-78911bc9717046e2b4361a6c496fb569';

export const SG_TEMPLATE_PASSWORD_RESET = 'd-ebe24529c13c40dc8872fa2778a9c2c4';
export const SG_TEMPLATE_VERIFY_EMAIL = 'd-45403d495d7c41cb8dd88cf0370af44f';
export const SG_TEMPLATE_WELCOME_TO_PRO_EMAIL = 'd-6fe77d60ad8c4bab88123b5a5fd6073b';
export const SG_TEMPLATE_WELCOME_TO_INDIE_EMAIL = 'd-52fe5d49f35b4d01b29cff1e0b40feba';

export const SG_TEMPLATE_CANCELLATION_CONFIRMATION_EMAIL = 'd-454101743b0248ab94c44b70b1a35235';
export const SG_TEMPLATE_CANCELLATION_OUTREACH_KEITH = 'd-265c85d385614d4ab3e727b33ac76c52';
export const SG_TEMPLATE_CANCELLATION_OUTREACH_SAM = 'd-807bca27442f49eabf0151fe9af58dfc';
export const SG_TEMPLATE_SUBSCRIPTION_ENDED_EMAIL = 'd-4cb0958cb3ba4543bbb7743ce0d08b45';
export const SG_TEMPLATE_ABANDONED_CART_REENGAGEMENT = 'd-7d48ae22b750441485d1724d9c2c56a3';
export const SG_TEMPLATE_STALE_PROJECT_REENGAGEMENT = 'd-3c644e5e91384d0d8aacb3a9a930a198';

export const SG_UNSUB_TRANSACTIONAL = 20765;
export const SG_UNSUB_PRODUCT_UPDATES = 20766;
export const SG_UNSUB_TIPS = 22528;
export const SG_UNSUB_FORGOT_PASSWORD = 22690;
export const SG_UNSUB_VERIFY_EMAIL = 22691;

export enum SendgridLists {
  NEW_USERS = '3fba6f3c-626f-4c33-8ff7-22b1b1d28c6e',
  RESTORED_USERS = '54754893-ed26-4baf-98b8-19df17487d97',
}

dotenv.config();

export const handleSendgridWebhook = declareHandler({
  middleware: bodyParser.json(),
  func: async (req, res) => {
    logger.info('Received Sendgrid webhook');

    for (const item of req.body) {
      if (!item.email) {
        res.status(400);
        res.send({ success: false, reason: 'Missing recipient email address' });
        return;
      }

      // https://docs.sendgrid.com/for-developers/tracking-events/event#the-resulting-webhook-call
      logger.info(`Inserting Sendgrid event: ${item.event} for ${item.email}`);
      try {
        await query(
          `
        INSERT INTO sendgrid_events (
          user_id,
          email,
          event,
          source,
          category,
          ip,
          mc_stats,
          send_at,
          sg_event_id,
          sg_message_id,
          sg_template_id,
          sg_template_name,
          singlesend_id,
          singlesend_name,
          marketing_campaign_id,
          marketing_campaign_name,
          marketing_campaign_version,
          template_hash,
          template_id,
          template_version_id,
          useragent
        ) VALUES (
          (SELECT id FROM users WHERE email = $1),
          $1,
          $2,
          'webhook',
          $3,
          $4,
          $5,
          $6,
          $7,
          $8,
          $9,
          $10,
          $11,
          $12,
          $13,
          $14,
          $15,
          $16,
          $17,
          $18,
          $19
        )
        `,
          [
            item.email,
            item.event,
            item.category,
            item.ip,
            item.mc_stats,
            item.send_at,
            item.sg_event_id,
            item.sg_message_id,
            item.sg_template_id,
            item.sg_template_name,
            item.singlesend_id,
            item.singlesend_name,
            item.marketing_campaign_id,
            item.marketing_campaign_name,
            item.marketing_campaign_version,
            item.template_hash,
            item.template_id,
            item.template_version_id,
            item.useragent,
          ]
        );
      } catch (e) {
        console.error('Error inserting Sendgrid event:', e);
        res.status(500);
        res.send({ success: false, reason: 'Error inserting Sendgrid event' });
        return;
      }
    }

    res.status(200);
    res.send({ success: true });
  },
});

export const recordSentEmail = async (
  email: string,
  templateId: string,
  templateName: string,
  sendAt?: number,
  sendgridMessageId?: string
) => {
  logger.info('Mail sent successfully!');

  await query(
    `
      INSERT INTO sendgrid_events (
        user_id,
        email,
        event,
        source,
        sg_template_id,
        sg_template_name,
        send_at,
        sg_message_id
      ) VALUES (
        (SELECT id FROM users WHERE email = $1),
        $1,
        'sent',
        'internal',
        $2,
        $3,
        $4,
        $5
      )
      `,
    [email, templateId, templateName, sendAt || Math.round(Date.now() / 1000), sendgridMessageId]
  );
};

const hasReceivedSameEmailWithinLast14Days = async (email: string, templateId: string): Promise<boolean> => {
  const fourteenDaysAgoEpochTime = Math.round((Date.now() - 14 * 24 * 60 * 60 * 1000) / 1000);
  const response = await query(
    `
          SELECT sg_template_name, send_at
          FROM sendgrid_events
          WHERE email = $1
          AND sg_template_id = $2
          AND event = 'sent'
          AND send_at > $3
          ORDER BY send_at DESC
        `,
    [email, templateId, fourteenDaysAgoEpochTime]
  );

  const { templateName, sendAt } = response?.rows?.[0] || {};
  logger.info(`'${templateName}' email was sent to ${email} on ${new Date(sendAt * 1000).toLocaleDateString()}.`);

  return !!templateName;
};

// Customer lifecycle email: Welcome (Pro)
const getProWelcomeEmailData = (user: { email: string; displayName: string }): MailDataRequired => ({
  from: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  replyTo: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  personalizations: [
    {
      to: [
        {
          email: user.email,
        },
      ],
      dynamicTemplateData: {
        displayName: user.displayName,
      },
    },
  ],
  templateId: SG_TEMPLATE_WELCOME_TO_PRO_EMAIL,
  asm: {
    groupId: SG_UNSUB_TRANSACTIONAL,
  },
});

export const sendProWelcomeEmailToUserId = (email, displayName) =>
  sendIfAllRecipientsEmailable(getProWelcomeEmailData({ email, displayName }))
    .then(() => logger.info('Mail sent successfully!'))
    .then(() => recordSentEmail(email, SG_TEMPLATE_WELCOME_TO_PRO_EMAIL, 'Welcome to Pro'))
    .catch((e) => {
      console.error(e);
      throw e;
    });

// Customer lifecycle email: Welcome (Indie)
const getIndieWelcomeEmailData = (user: { email: string; displayName: string }): MailDataRequired => ({
  from: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  replyTo: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  personalizations: [
    {
      to: [
        {
          email: user.email,
        },
      ],
      dynamicTemplateData: {
        displayName: user.displayName,
      },
    },
  ],
  templateId: SG_TEMPLATE_WELCOME_TO_INDIE_EMAIL,
  asm: {
    groupId: SG_UNSUB_TRANSACTIONAL,
  },
});

export const sendIndieWelcomeEmailToUserId = (email, displayName) =>
  sendIfAllRecipientsEmailable(getIndieWelcomeEmailData({ email, displayName }))
    .then(() => logger.info('Mail sent successfully!'))
    .then(() => recordSentEmail(email, SG_TEMPLATE_WELCOME_TO_INDIE_EMAIL, 'Welcome to Indie'))
    .catch((e) => {
      console.error(e);
      throw e;
    });

// Customer lifecycle email: Cancellation confirmation (sent immediately after user cancels)
const getCancellationConfirmationEmailData = (user: {
  email: string;
  name: string;
  subscribedUntil: Date;
  tier: string;
}): MailDataRequired => ({
  from: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  replyTo: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  personalizations: [
    {
      to: [
        {
          email: user.email,
        },
      ],
      dynamicTemplateData: {
        display_name: user.name,
        subscription_end_date: user.subscribedUntil.toLocaleDateString(undefined, {
          month: 'short',
          day: 'numeric',
          year: 'numeric',
        }),
        tier: user.tier,
      },
    },
  ],
  templateId: SG_TEMPLATE_CANCELLATION_CONFIRMATION_EMAIL,
  asm: {
    groupId: SG_UNSUB_PRODUCT_UPDATES,
  },
});

export const sendCancellationConfirmationEmailToUser = async (email, name, subscribedUntil, tier) => {
  const hasReceivedEmail = await hasReceivedSameEmailWithinLast14Days(
    email,
    SG_TEMPLATE_CANCELLATION_CONFIRMATION_EMAIL
  );
  if (hasReceivedEmail) {
    logger.info('Email has already been sent within the last 14 days. Aborting send.');
    return;
  }

  sendIfAllRecipientsEmailable(
    getCancellationConfirmationEmailData({
      email,
      name,
      subscribedUntil,
      tier,
    })
  )
    .then(() => logger.info('Cancellation confirmation email sent successfully!'))
    .then(() => recordSentEmail(email, SG_TEMPLATE_CANCELLATION_CONFIRMATION_EMAIL, 'Cancellation Confirmation Email'))
    .catch((e) => {
      console.error(e);
      throw e;
    });
};

// Customer lifecycle email: Cancellation outreach from founder (sent 30 minutes after user cancels)
const cancellationOutreachSenders = [
  {
    email: 'keith@wavtool.com',
    name: 'Keith',
    template: SG_TEMPLATE_CANCELLATION_OUTREACH_KEITH,
  },
  {
    email: 'sam@wavtool.com',
    name: 'Sam',
    template: SG_TEMPLATE_CANCELLATION_OUTREACH_SAM,
  },
];

const getFoundersCancellationOutreachEmailData = (user: {
  email: string;
  name: string;
  id: number;
}): MailDataRequired => {
  const sender = cancellationOutreachSenders[user.id % cancellationOutreachSenders.length];
  return {
    from: {
      email: sender.email,
    },
    replyTo: {
      email: sender.email,
      name: sender.name,
    },
    personalizations: [
      {
        to: [
          {
            email: user.email,
          },
        ],
        dynamicTemplateData: {
          display_name: user.name,
        },
      },
    ],
    templateId: sender.template,
    asm: {
      groupId: SG_UNSUB_PRODUCT_UPDATES,
    },
    sendAt: Math.round((Date.now() + 1000 * 60 * 30) / 1000), // delay 30 minutes before sending
  };
};

export const sendFoundersCancellationOutreachEmailToUser = async (email, name, id) => {
  const hasReceivedEmail = await hasReceivedSameEmailWithinLast14Days(
    email,
    cancellationOutreachSenders[id % cancellationOutreachSenders.length].template
  );
  if (hasReceivedEmail) {
    logger.info('Email has already been sent within the last 14 days. Aborting send.');
    return;
  }

  sendIfAllRecipientsEmailable(
    getFoundersCancellationOutreachEmailData({
      email,
      name,
      id,
    })
  )
    .then(() => logger.info("Founders' cancellation outreach email sent successfully!"))
    .then(() =>
      recordSentEmail(
        email,
        cancellationOutreachSenders[id % cancellationOutreachSenders.length].template,
        `Founders' Cancellation Outreach Email (${
          cancellationOutreachSenders[id % cancellationOutreachSenders.length].name
        })`,
        Math.round((Date.now() + 1000 * 60 * 30) / 1000),
        ''
      )
    )
    .catch((e) => {
      console.error(e);
      throw e;
    });
};

// Customer lifecycle email: Subscription ended (sent at end of billing period, after user cancels)
const getSubscriptionEndedEmailData = (user: { email: string; name: string; tier: string }): MailDataRequired => ({
  from: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  replyTo: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  personalizations: [
    {
      to: [
        {
          email: user.email,
        },
      ],
      dynamicTemplateData: {
        display_name: user.name,
        tier: user.tier,
      },
    },
  ],
  templateId: SG_TEMPLATE_SUBSCRIPTION_ENDED_EMAIL,
  asm: {
    groupId: SG_UNSUB_PRODUCT_UPDATES,
  },
});

export const sendSubscriptionEndedEmailToUser = async (email, name, tier) => {
  const hasReceivedEmail = await hasReceivedSameEmailWithinLast14Days(email, SG_TEMPLATE_SUBSCRIPTION_ENDED_EMAIL);
  if (hasReceivedEmail) {
    logger.info('Email has already been sent within the last 14 days. Aborting send.');
    return;
  }

  sendIfAllRecipientsEmailable(
    getSubscriptionEndedEmailData({
      email,
      name,
      tier,
    })
  )
    .then(() => logger.info('Subscription ended email sent successfully!'))
    .then(() => recordSentEmail(email, SG_TEMPLATE_SUBSCRIPTION_ENDED_EMAIL, 'Subscription Ended Email'))
    .catch((e) => {
      console.error(e);
      throw e;
    });
};

// Reengagement email: Abandoned cart
const getAbandonedCartReengagementEmailData = (user: { email: string; firstName: string }): MailDataRequired => ({
  from: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  replyTo: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  personalizations: [
    {
      to: [
        {
          email: user.email,
        },
      ],
      dynamicTemplateData: {
        first_name: user.firstName,
      },
    },
  ],
  templateId: SG_TEMPLATE_ABANDONED_CART_REENGAGEMENT,
  asm: {
    groupId: SG_UNSUB_PRODUCT_UPDATES,
  },
});

export const sendAbandonedCartReengagementEmailToUser = async (email, firstName) => {
  const hasReceivedEmail = await hasReceivedSameEmailWithinLast14Days(email, SG_TEMPLATE_ABANDONED_CART_REENGAGEMENT);
  if (hasReceivedEmail) {
    logger.info('Email has already been sent within the last 14 days. Aborting send.');
    return;
  }

  sendIfAllRecipientsEmailable(
    getAbandonedCartReengagementEmailData({
      email,
      firstName,
    })
  )
    .then(() => logger.info('Abandoned cart reengagement email sent successfully!'))
    .then(() => recordSentEmail(email, SG_TEMPLATE_ABANDONED_CART_REENGAGEMENT, 'Abandoned Cart Reengagement Email'))
    .catch((e) => {
      console.error(e);
      throw e;
    });
};

// Reengagement email: Stale projects
const getStaleProjectReengagementEmailData = (user: {
  email: string;
  name: string;
  projectName: string;
}): MailDataRequired => ({
  from: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  replyTo: {
    email: 'hello@wavtool.com',
    name: 'WavTool Team',
  },
  personalizations: [
    {
      to: [
        {
          email: user.email,
        },
      ],
      dynamicTemplateData: {
        display_name: user.name,
        project_name: user.projectName,
      },
    },
  ],
  templateId: SG_TEMPLATE_STALE_PROJECT_REENGAGEMENT,
  asm: {
    groupId: SG_UNSUB_PRODUCT_UPDATES,
  },
});

export const sendStaleProjectReengagementEmailToUser = async (email, name, projectName) =>
  await sendIfAllRecipientsEmailable(
    getStaleProjectReengagementEmailData({
      email,
      name,
      projectName,
    })
  )
    .then(() => logger.info('Stale project reengagement email sent successfully!'))
    .then(() => recordSentEmail(email, SG_TEMPLATE_STALE_PROJECT_REENGAGEMENT, 'Stale Project Reengagement Email'))
    .catch((e) => {
      console.error(e);
      throw e;
    });

// Helper function to get user's first name from their Google profile
export const getUserFirstName = async (userId: string) => {
  try {
    const user_identity = await query(
      `
            SELECT profile_data
            FROM user_identities ui
            JOIN users u
            ON ui.user_id = u.id
            WHERE u.id = $1
            AND ui.issuer LIKE '%google%'
          `,
      [userId]
    );
    const profileData = user_identity?.rows?.[0]?.profile_data;
    if (!profileData) return null;
    return JSON.parse(profileData)?.name?.givenName;
  } catch (error) {
    console.error(error);
    Sentry.captureException(error);
    return null;
  }
};
