// src/services/contact.ts
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
// All Rights Reserved

import env from "../config/env.js";
// import assert from "node:assert";
import crypto from "node:crypto";

import nodemailer from "nodemailer";
import Mail from "nodemailer/lib/mailer";

import EmailLog, { IEmailLog } from "../models/email-log.js";
import EmailVerification, {
  EmailVerificationStatus,
  IEmailVerification,
} from "../models/email-verification.js";
import { IUser } from "@gadget/api";

import UserService from "./user.js";
import { DtpService } from "../lib/service.js";
import { PopulateOptions } from "mongoose";

export type EmailAddress = Mail.Address | string;

export interface EmailMessage {
  from: EmailAddress;
  replyTo?: EmailAddress;
  to: EmailAddress;
  cc?: EmailAddress;
  subject: string;
  text?: string;
  html?: string;
  attachments?: Array<Mail.Attachment>;
}

class ContactService extends DtpService {
  private transport?: nodemailer.Transporter;
  private populateEmailVerification: PopulateOptions[] = [];

  get name(): string {
    return "ContactService";
  }
  get slug(): string {
    return "contact";
  }

  async start(): Promise<void> {
    this.log.info("starting");
    this.log.info("creating SMTP transport", {
      host: env.email.smtp.host,
      port: env.email.smtp.port,
    });
    const transportOptions = {
      host: env.email.smtp.host,
      port: env.email.smtp.port,
      secure: env.email.smtp.secure,
      auth: {
        user: env.email.smtp.user,
        pass: env.email.smtp.password,
      },
      pool: env.email.smtp.pool.enabled,
      maxConnections: env.email.smtp.pool.maxConnections,
      maxMessages: env.email.smtp.pool.maxMessages,
    };
    this.transport = nodemailer.createTransport(transportOptions);

    this.populateEmailVerification = [
      {
        path: "user",
      },
    ];
  }

  async stop(): Promise<void> {
    this.log.info("stopped");
  }

  async sendEmail(message: EmailMessage): Promise<IEmailLog> {
    if (!this.transport) {
      const error = new Error("Email transport required");
      error.statusCode = 503;
      throw error;
    }

    const NOW = new Date();

    this.log.info("sending email", { to: message.to });
    const response = await this.transport.sendMail(message);
    this.log.debug("email sent", { to: message.to, response });

    const from =
      typeof message.from === "object" ? message.from.address : message.from;
    const to = typeof message.to === "object" ? message.to.address : message.to;
    const log = await EmailLog.create({
      created: NOW,
      from,
      to,
      to_lc: to.toLowerCase(),
      subject: message.subject,
      messageId: response.messageId,
    });

    return log.toObject();
  }

  sendVerificationEmail(user: IUser): Promise<IEmailLog> {
    const NOW = new Date();

    const verification = new EmailVerification();
    verification.createdAt = NOW;
    verification.code = crypto.randomBytes(20).toString("hex").toUpperCase();
    verification.status = EmailVerificationStatus.Pending;
    verification.user = user._id;

    const verificationLink = `https://${env.site.host}/auth/email-verify?code=${verification.code}`;
    const message: EmailMessage = {
      from: env.email.contact.to,
      to: user.email,
      subject: `Please verify your email address (${env.site.name})`,
      text: `Hello ${user.displayName},

Please verify your email address for use at ${env.site.name} by clicking the link below:

${verificationLink}

If you did not create an account with us, please ignore this email.

Thank you,
The ${env.site.name} Team
`,
    };

    return this.sendEmail(message);
  }

  async verifyEmailCode(code: string): Promise<IEmailVerification> {
    const verification = await EmailVerification.findOne({ code })
      .populate(this.populateEmailVerification)
      .lean();
    if (!verification) {
      const error = new Error("Invalid verification code");
      error.statusCode = 400;
      throw error;
    }

    if (verification.status !== EmailVerificationStatus.Pending) {
      const error = new Error(
        `Verification code is not pending (status: ${verification.status})`,
      );
      error.statusCode = 400;
      throw error;
    }

    const NOW = new Date();
    const elapsedMs = NOW.getTime() - verification.createdAt.getTime();
    const elapsedHours = elapsedMs / (1000 * 60 * 60);
    if (elapsedHours > 24) {
      const error = new Error("Verification code has expired");
      error.statusCode = 400;
      throw error;
    }

    verification.status = EmailVerificationStatus.Verified;
    const newVerification = await EmailVerification.findOneAndUpdate(
      { _id: verification._id },
      {
        status: EmailVerificationStatus.Verified,
      },
      { new: true, populate: this.populateEmailVerification, lean: true },
    );
    if (!newVerification) {
      const error = new Error("Failed to update verification status");
      error.statusCode = 500;
      throw error;
    }

    await UserService.setEmailVerified(verification.user as IUser, true);

    return newVerification;
  }
}

export default new ContactService();
