import * as plugins from '../../plugins.js';
import { CachedDocument, TTL } from '../classes.cached.document.js';
import { DcRouterDb } from '../classes.dcrouter-db.js';

/**
 * Email status in the cache
 */
export type TCachedEmailStatus = 'pending' | 'processing' | 'delivered' | 'failed' | 'deferred';

/**
 * Helper to get the smartdata database instance
 */
const getDb = () => DcRouterDb.getInstance().getDb();

/**
 * CachedEmail - Stores email queue items in the cache
 *
 * Used for persistent email queue storage, tracking delivery status,
 * and maintaining email history for the configured TTL period.
 */
@plugins.smartdata.Collection(() => getDb())
export class CachedEmail extends CachedDocument<CachedEmail> {
  // TTL fields from base class (decorators required on concrete class)
  @plugins.smartdata.svDb()
  public createdAt: Date = new Date();

  @plugins.smartdata.svDb()
  public expiresAt: Date = new Date(Date.now() + TTL.DAYS_30);

  @plugins.smartdata.svDb()
  public lastAccessedAt: Date = new Date();

  /**
   * Unique identifier for this email
   */
  @plugins.smartdata.unI()
  @plugins.smartdata.svDb()
  public id!: string;

  /**
   * Email message ID (RFC 822 Message-ID header)
   */
  @plugins.smartdata.svDb()
  public messageId!: string;

  /**
   * Sender email address (envelope from)
   */
  @plugins.smartdata.svDb()
  public from!: string;

  /**
   * Recipient email addresses
   */
  @plugins.smartdata.svDb()
  public to!: string[];

  /**
   * CC recipients
   */
  @plugins.smartdata.svDb()
  public cc!: string[];

  /**
   * BCC recipients
   */
  @plugins.smartdata.svDb()
  public bcc!: string[];

  /**
   * Email subject
   */
  @plugins.smartdata.svDb()
  public subject!: string;

  /**
   * Raw RFC822 email content
   */
  @plugins.smartdata.svDb()
  public rawContent!: string;

  /**
   * Current status of the email
   */
  @plugins.smartdata.svDb()
  public status!: TCachedEmailStatus;

  /**
   * Number of delivery attempts
   */
  @plugins.smartdata.svDb()
  public attempts: number = 0;

  /**
   * Maximum number of delivery attempts
   */
  @plugins.smartdata.svDb()
  public maxAttempts: number = 3;

  /**
   * Timestamp for next delivery attempt
   */
  @plugins.smartdata.svDb()
  public nextAttempt!: Date;

  /**
   * Last error message if delivery failed
   */
  @plugins.smartdata.svDb()
  public lastError!: string;

  /**
   * Timestamp when the email was successfully delivered
   */
  @plugins.smartdata.svDb()
  public deliveredAt!: Date;

  /**
   * Sender domain (for querying/filtering)
   */
  @plugins.smartdata.svDb()
  public senderDomain!: string;

  /**
   * Priority level (higher = more important)
   */
  @plugins.smartdata.svDb()
  public priority: number = 0;

  /**
   * JSON-serialized route data
   */
  @plugins.smartdata.svDb()
  public routeData!: string;

  /**
   * DKIM signature status
   */
  @plugins.smartdata.svDb()
  public dkimSigned: boolean = false;

  constructor() {
    super();
    this.setTTL(TTL.DAYS_30); // Default 30-day TTL
    this.status = 'pending';
    this.to = [];
    this.cc = [];
    this.bcc = [];
  }

  /**
   * Create a new CachedEmail with a unique ID
   */
  public static createNew(): CachedEmail {
    const email = new CachedEmail();
    email.id = plugins.uuid.v4();
    return email;
  }

  /**
   * Find an email by ID
   */
  public static async findById(id: string): Promise<CachedEmail | null> {
    return await CachedEmail.getInstance({
      id,
    });
  }

  /**
   * Find all emails with a specific status
   */
  public static async findByStatus(status: TCachedEmailStatus): Promise<CachedEmail[]> {
    return await CachedEmail.getInstances({
      status,
    });
  }

  /**
   * Find all emails pending delivery (status = pending and nextAttempt <= now)
   */
  public static async findPendingForDelivery(): Promise<CachedEmail[]> {
    const now = new Date();
    return await CachedEmail.getInstances({
      status: 'pending',
      nextAttempt: { $lte: now },
    });
  }

  /**
   * Find emails by sender domain
   */
  public static async findBySenderDomain(domain: string): Promise<CachedEmail[]> {
    return await CachedEmail.getInstances({
      senderDomain: domain,
    });
  }

  /**
   * Mark as delivered
   */
  public markDelivered(): void {
    this.status = 'delivered';
    this.deliveredAt = new Date();
  }

  /**
   * Mark as failed with error
   */
  public markFailed(error: string): void {
    this.status = 'failed';
    this.lastError = error;
  }

  /**
   * Increment attempt counter and schedule next attempt
   */
  public scheduleRetry(delayMs: number = 5 * 60 * 1000): void {
    this.attempts++;
    this.status = 'deferred';
    this.nextAttempt = new Date(Date.now() + delayMs);

    // If max attempts reached, mark as failed
    if (this.attempts >= this.maxAttempts) {
      this.status = 'failed';
      this.lastError = `Max attempts (${this.maxAttempts}) reached`;
    }
  }

  /**
   * Extract sender domain from email address
   */
  public updateSenderDomain(): void {
    if (this.from) {
      const match = this.from.match(/@([^>]+)>?$/);
      if (match) {
        this.senderDomain = match[1].toLowerCase();
      }
    }
  }
}
