import fs from "fs";
import path from "path";
import nodemailer from "nodemailer";
import handlebars from "handlebars";
import { logger } from "../../common/utils/logger";
import { env } from "../../config/env";

export interface SendMailOptions {
  to: string;
  subject: string;
  text?: string;
  html?: string;
}

class MailService {
  private transporter: nodemailer.Transporter | null = null;
  private templateCache: Map<string, handlebars.TemplateDelegate> = new Map();

  constructor() {
    if (env.SMTP_HOST && env.SMTP_USER && env.SMTP_PASS) {
      this.transporter = nodemailer.createTransport({
        host: env.SMTP_HOST,
        port: env.SMTP_PORT || 587,
        secure: env.SMTP_PORT === 465,
        auth: {
          user: env.SMTP_USER,
          pass: env.SMTP_PASS,
        },
      });
      logger.info(
        "MailService initialized with SMTP transport and Handlebars templating.",
      );
    } else {
      logger.warn(
        "SMTP credentials not configured. MailService running in development/console log mode.",
      );
    }
  }

  private renderTemplate(
    templateName: string,
    context: Record<string, unknown>,
  ): string {
    let template = this.templateCache.get(templateName);
    if (!template) {
      // Resolve path carefully depending on where the compiled javascript runs relative to templates
      const templatePath = path.join(
        __dirname,
        "templates",
        `${templateName}.hbs`,
      );
      try {
        const source = fs.readFileSync(templatePath, "utf8");
        template = handlebars.compile(source);
        this.templateCache.set(templateName, template);
      } catch (error) {
        logger.error(
          { error, templatePath },
          `Failed to read or compile Handlebars template: ${templateName}`,
        );
        throw new Error(`Email template ${templateName} not found or invalid`);
      }
    }
    return template(context);
  }

  async sendMail(options: SendMailOptions): Promise<boolean> {
    const from = env.SMTP_FROM || "noreply@sgms.com";

    if (this.transporter) {
      try {
        await this.transporter.sendMail({
          from,
          to: options.to,
          subject: options.subject,
          text: options.text,
          html: options.html,
        });
        logger.info(
          `Email sent successfully to ${options.to} [Subject: ${options.subject}]`,
        );
        return true;
      } catch (error) {
        logger.error(
          { error, to: options.to, subject: options.subject },
          "Failed to send email via SMTP",
        );
        return false;
      }
    } else {
      // Development console fallback
      logger.info(
        {
          to: options.to,
          subject: options.subject,
          text: options.text,
        },
        `[DEV MAIL DISPATCH] To: ${options.to} | Subject: ${options.subject}`,
      );
      return true;
    }
  }

  async sendInvitationEmail(data: {
    to: string;
    name: string;
    email: string;
    role: string;
    department?: string;
    inviteLink: string;
    expiresInHours: number;
  }): Promise<boolean> {
    const subject = `You're invited to join TaxFend`;
    const html = this.renderTemplate("invitation", {
      name: data.name,
      email: data.email,
      role: data.role,
      department: data.department,
      inviteLink: data.inviteLink,
      expiresInHours: data.expiresInHours,
      companyName: "TaxFend",
      currentYear: new Date().getFullYear(),
    });
    const text = `Hi ${data.name}, you have been invited to join TaxFend as ${data.role}. Accept your invitation: ${data.inviteLink} (expires in ${data.expiresInHours} hours)`;
    return this.sendMail({ to: data.to, subject, text, html });
  }

  async sendTaskCompletedEmail(data: {
    to: string;
    clientName: string;
    clientPhone?: string;
    clientGender?: string;
    taskTitle: string;
    serviceName: string;
    completedAt: string;
    documents: { fileName: string; fileUrl: string }[];
    invoice?: {
      invoiceNumber: string;
      subtotal: string;
      gstAmount?: string;
      totalAmount: string;
    };
    companyName: string;
    taskUrl: string;
    invoiceUrl?: string;
  }): Promise<boolean> {
    const subject = `Your task "${data.taskTitle}" has been completed — ${data.companyName}`;
    const html = this.renderTemplate("task-completed", {
      ...data,
      currentYear: new Date().getFullYear(),
    });
    const text = `Dear ${data.clientName}, your task "${data.taskTitle}" has been completed. Please check your documents and invoice.`;
    return this.sendMail({ to: data.to, subject, text, html });
  }

  async sendOtpEmail(
    email: string,
    otp: string,
    purpose: string,
  ): Promise<boolean> {
    const subject = `TaxFend - ${purpose} Verification Code`;

    const text = `Your TaxFend verification code for ${purpose} is ${otp}. This code will expire in 15 minutes. If you did not request this code, please ignore this email.`;

    const html = this.renderTemplate("otp", {
      purpose,
      otp,
      expiresInMinutes: 15,
      companyName: "TaxFend",
      currentYear: new Date().getFullYear(),
    });

    return this.sendMail({ to: email, subject, text, html });
  }

  async sendTaskRejectedEmail(data: {
    to: string;
    clientName: string;
    taskTitle: string;
    serviceName: string;
    remarks?: string;
    rejectedAt: string;
    companyName: string;
    taskUrl: string;
  }): Promise<boolean> {
    const subject = `Your task "${data.taskTitle}" was rejected — ${data.companyName}`;
    const html = this.renderTemplate("task-rejected", {
      ...data,
      currentYear: new Date().getFullYear(),
    });
    const text = `Dear ${data.clientName}, your task "${data.taskTitle}" has been rejected by the manager. Reason: ${data.remarks || "N/A"}. Please check the portal for details.`;
    return this.sendMail({ to: data.to, subject, text, html });
  }

  async sendTaskAssignedEmail(data: {
    to: string;
    employeeName: string;
    taskTitle: string;
    serviceName?: string;
    dueDate?: string;
    priority?: string;
    companyName: string;
    taskUrl: string;
  }): Promise<boolean> {
    const subject = `New task assigned: "${data.taskTitle}" — ${data.companyName}`;
    const html = this.renderTemplate("task-assigned", {
      ...data,
      currentYear: new Date().getFullYear(),
    });
    const text = `Hi ${data.employeeName}, you have been assigned a new task "${data.taskTitle}"${data.dueDate ? ` (due ${data.dueDate})` : ""}. Please check the portal for details.`;
    return this.sendMail({ to: data.to, subject, text, html });
  }

  async sendNotificationEmail(data: {
    to: string;
    userName: string;
    title: string;
    message: string;
    actionUrl?: string;
    actionLabel?: string;
    companyName: string;
  }): Promise<boolean> {
    const subject = `${data.title} — ${data.companyName}`;
    const html = this.renderTemplate("notification", {
      ...data,
      currentYear: new Date().getFullYear(),
    });
    const text = `Hello ${data.userName}, ${data.message}`;
    return this.sendMail({ to: data.to, subject, text, html });
  }

  async sendDocumentRejectedEmail(data: {
    to: string;
    clientName: string;
    taskTitle: string;
    serviceName: string;
    rejectedDocuments: { fileName: string; remarks?: string }[];
    rejectedAt: string;
    companyName: string;
    taskUrl: string;
  }): Promise<boolean> {
    const subject = `Action needed: documents rejected for "${data.taskTitle}" — ${data.companyName}`;
    const html = this.renderTemplate("document-rejected", {
      ...data,
      currentYear: new Date().getFullYear(),
    });
    const text = `Dear ${data.clientName}, some documents for your task "${data.taskTitle}" were rejected. Please check the portal and re-upload.`;
    return this.sendMail({ to: data.to, subject, text, html });
  }
}
export const mailService = new MailService();
