import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { SendGridEmailProvider } from "./providers/sendgrid.provider.js";
import { EmailQueueService } from "./queue/email.queue.js";
import type { EmailJobData } from "./queue/email.queue.js";

export interface SendEmailOptions {
  to: string | string[];
  from?: string;
  /** BCC recipients - useful for Email-to-Case integration */
  bcc?: string | string[];
  subject: string;
  text?: string;
  html?: string;
  templateId?: string;
  dynamicTemplateData?: Record<string, unknown>;
  /** Category for tracking (e.g., 'otp', 'password-reset', 'welcome') */
  category?: EmailCategory;
}

/** Predefined email categories for consistent tracking */
export type EmailCategory =
  | "otp-verification"
  | "password-reset"
  | "welcome"
  | "eligibility-submitted"
  | "account-notification"
  | "transactional";

export interface SendEmailResult {
  success: boolean;
  queued: boolean;
  error?: string;
}

@Injectable()
export class EmailService {
  private readonly emailEnabled: boolean;
  private readonly useQueue: boolean;

  constructor(
    private readonly config: ConfigService,
    private readonly provider: SendGridEmailProvider,
    private readonly queue: EmailQueueService,
    @Inject(Logger) private readonly logger: Logger
  ) {
    this.emailEnabled = this.config.get("EMAIL_ENABLED", "true") === "true";
    this.useQueue = this.config.get("EMAIL_USE_QUEUE", "true") === "true";

    this.logger.log("EmailService initialized", {
      service: "email",
      enabled: this.emailEnabled,
      useQueue: this.useQueue,
    });
  }

  async sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {
    const emailContext = {
      service: "email",
      to: this.getRecipientCount(options.to),
      subject: options.subject,
      category: options.category || "transactional",
      hasTemplate: !!options.templateId,
    };

    // Validate required fields
    const validationError = this.validateOptions(options);
    if (validationError) {
      this.logger.error("Email validation failed", {
        ...emailContext,
        error: validationError,
      });
      return { success: false, queued: false, error: validationError };
    }

    // Check if email is enabled
    if (!this.emailEnabled) {
      this.logger.log("Email sending disabled - skipping", emailContext);
      return { success: true, queued: false };
    }

    // Add category to options for downstream tracking
    const enrichedOptions: EmailJobData = {
      ...options,
      category: options.category || "transactional",
    };

    try {
      if (this.useQueue) {
        await this.queue.enqueueEmail(enrichedOptions);
        this.logger.log("Email queued for delivery", emailContext);
        return { success: true, queued: true };
      }

      // Direct send (synchronous)
      await this.provider.send(enrichedOptions);
      this.logger.log("Email sent directly", emailContext);
      return { success: true, queued: false };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);

      this.logger.error("Failed to send/queue email", {
        ...emailContext,
        error: errorMessage,
        mode: this.useQueue ? "queued" : "direct",
      });

      return { success: false, queued: false, error: errorMessage };
    }
  }

  /**
   * Send email directly without queue (useful for critical emails)
   * Use sparingly - prefer queued delivery for reliability
   */
  async sendEmailDirect(options: SendEmailOptions): Promise<SendEmailResult> {
    const emailContext = {
      service: "email",
      mode: "direct-forced",
      to: this.getRecipientCount(options.to),
      subject: options.subject,
      category: options.category || "transactional",
    };

    const validationError = this.validateOptions(options);
    if (validationError) {
      this.logger.error("Email validation failed", { ...emailContext, error: validationError });
      return { success: false, queued: false, error: validationError };
    }

    if (!this.emailEnabled) {
      this.logger.log("Email sending disabled - skipping direct send", emailContext);
      return { success: true, queued: false };
    }

    try {
      await this.provider.send({
        ...options,
        category: options.category || "transactional",
      });
      this.logger.log("Email sent directly (forced)", emailContext);
      return { success: true, queued: false };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      this.logger.error("Direct email send failed", { ...emailContext, error: errorMessage });
      return { success: false, queued: false, error: errorMessage };
    }
  }

  private validateOptions(options: SendEmailOptions): string | null {
    if (!options.to || (Array.isArray(options.to) && options.to.length === 0)) {
      return "Recipient (to) is required";
    }

    if (!options.subject || options.subject.trim().length === 0) {
      return "Subject is required";
    }

    // Must have either template or content
    if (!options.templateId && !options.html && !options.text) {
      return "Email must have templateId, html, or text content";
    }

    // Basic email format validation
    const emails = Array.isArray(options.to) ? options.to : [options.to];
    for (const email of emails) {
      if (!this.isValidEmailFormat(email)) {
        return `Invalid email format: ${email}`;
      }
    }

    return null;
  }

  private isValidEmailFormat(email: string): boolean {
    // Stricter email validation to prevent queue pollution with invalid emails
    // Catches: multiple @, dots at start/end, consecutive dots, invalid domain
    const emailRegex =
      /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
    return emailRegex.test(email);
  }

  private getRecipientCount(to: string | string[]): number {
    return Array.isArray(to) ? to.length : 1;
  }
}
