import * as plugins from '../../plugins.js';
import type { OpsServer } from '../classes.opsserver.js';
import * as interfaces from '../../../ts_interfaces/index.js';
import { requireOpsAuth } from '../helpers/auth.js';

export class EmailOpsHandler {
  constructor(private opsServerRef: OpsServer) {
    this.registerHandlers();
  }

  private registerHandlers(): void {
    const viewRouter = this.opsServerRef.viewRouter;
    const adminRouter = this.opsServerRef.adminRouter;

    // ---- Read endpoints (viewRouter — valid identity required via middleware) ----

    // Get All Emails Handler
    viewRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetAllEmails>(
        'getAllEmails',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'emails:read' });
          const emails = this.getAllQueueEmails();
          return { emails };
        }
      )
    );

    // Get Email Detail Handler
    viewRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetEmailDetail>(
        'getEmailDetail',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, { scope: 'emails:read' });
          const email = this.getEmailDetail(dataArg.emailId);
          return { email };
        }
      )
    );

    // ---- Write endpoints (adminRouter) ----

    // Resend Failed Email Handler
    adminRouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_ResendEmail>(
        'resendEmail',
        async (dataArg) => {
          await requireOpsAuth(this.opsServerRef, dataArg, {
            scope: 'emails:write',
            requireAdminIdentity: true,
          });
          const emailServer = this.opsServerRef.dcRouterRef.emailServer;
          if (!emailServer?.deliveryQueue) {
            return { success: false, error: 'Email server not available' };
          }

          const queue = emailServer.deliveryQueue;
          const item = emailServer.getQueueItem(dataArg.emailId);

          if (!item) {
            return { success: false, error: 'Email not found in queue' };
          }

          if (item.status !== 'failed') {
            return { success: false, error: `Email is not in failed state (current: ${item.status})` };
          }

          try {
            const newQueueId = await queue.enqueue(
              item.processingResult,
              item.processingMode,
              item.route
            );
            await queue.removeItem(dataArg.emailId);
            return { success: true, newQueueId };
          } catch (error) {
            return {
              success: false,
              error: error instanceof Error ? error.message : 'Failed to resend email'
            };
          }
        }
      )
    );
  }

  /**
   * Get all queue items mapped to catalog IEmail format
   */
  private getAllQueueEmails(): interfaces.requests.IEmail[] {
    const emailServer = this.opsServerRef.dcRouterRef.emailServer;
    if (!emailServer) {
      return [];
    }
    const emails = emailServer.getQueueItems().map((item) => this.mapQueueItemToEmail(item));

    // Sort by createdAt descending (newest first)
    emails.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());

    return emails;
  }

  /**
   * Get a single email detail by ID
   */
  private getEmailDetail(emailId: string): interfaces.requests.IEmailDetail | null {
    const emailServer = this.opsServerRef.dcRouterRef.emailServer;
    if (!emailServer) {
      return null;
    }
    const item = emailServer.getQueueItem(emailId);

    if (!item) {
      return null;
    }

    return this.mapQueueItemToEmailDetail(item);
  }

  /**
   * Map a queue item to catalog IEmail format
   */
  private mapQueueItemToEmail(item: any): interfaces.requests.IEmail {
    const processingResult = item.processingResult;
    let from = '';
    let to = '';
    let subject = '';
    let messageId = '';
    let size = '0 B';

    if (processingResult) {
      if (processingResult.email) {
        from = processingResult.email.from || '';
        to = (processingResult.email.to || [])[0] || '';
        subject = processingResult.email.subject || '';
      } else if (processingResult.from) {
        from = processingResult.from;
        to = (processingResult.to || [])[0] || '';
        subject = processingResult.subject || '';
      }

      // Try to get messageId
      if (typeof processingResult.getMessageId === 'function') {
        try {
          messageId = processingResult.getMessageId() || '';
        } catch {
          messageId = '';
        }
      }

      // Compute approximate size
      const textLen = processingResult.text?.length || 0;
      const htmlLen = processingResult.html?.length || 0;
      let attachSize = 0;
      if (typeof processingResult.getAttachmentsSize === 'function') {
        try {
          attachSize = processingResult.getAttachmentsSize() || 0;
        } catch {
          attachSize = 0;
        }
      }
      size = this.formatSize(textLen + htmlLen + attachSize);
    }

    // Map queue status to catalog TEmailStatus
    const status = this.mapStatus(item.status);

    const createdAt = item.createdAt instanceof Date ? item.createdAt.getTime() : item.createdAt;

    return {
      id: item.id,
      direction: 'outbound' as interfaces.requests.TEmailDirection,
      status,
      from,
      to,
      subject,
      timestamp: new Date(createdAt).toISOString(),
      messageId,
      size,
    };
  }

  /**
   * Map a queue item to catalog IEmailDetail format
   */
  private mapQueueItemToEmailDetail(item: any): interfaces.requests.IEmailDetail {
    const base = this.mapQueueItemToEmail(item);
    const processingResult = item.processingResult;

    let toList: string[] = [];
    let cc: string[] = [];
    let headers: Record<string, string> = {};
    let body = '';

    if (processingResult) {
      if (processingResult.email) {
        toList = processingResult.email.to || [];
        cc = processingResult.email.cc || [];
      } else {
        toList = processingResult.to || [];
        cc = processingResult.cc || [];
      }

      headers = processingResult.headers || {};
      body = processingResult.html || processingResult.text || '';
    }

    return {
      ...base,
      toList,
      cc,
      smtpLog: [],
      connectionInfo: {
        sourceIp: '',
        sourceHostname: '',
        destinationIp: '',
        destinationPort: 0,
        tlsVersion: '',
        tlsCipher: '',
        authenticated: false,
        authMethod: '',
        authUser: '',
      },
      authenticationResults: {
        spf: 'none',
        spfDomain: '',
        dkim: 'none',
        dkimDomain: '',
        dmarc: 'none',
        dmarcPolicy: '',
      },
      rejectionReason: item.status === 'failed' ? item.lastError : undefined,
      bounceMessage: item.status === 'failed' ? item.lastError : undefined,
      headers,
      body,
    };
  }

  /**
   * Map queue status to catalog TEmailStatus
   */
  private mapStatus(queueStatus: string): interfaces.requests.TEmailStatus {
    switch (queueStatus) {
      case 'pending':
      case 'processing':
        return 'pending';
      case 'delivered':
        return 'delivered';
      case 'failed':
        return 'bounced';
      case 'deferred':
        return 'deferred';
      default:
        return 'pending';
    }
  }

  /**
   * Format byte size to human-readable string
   */
  private formatSize(bytes: number): string {
    if (bytes < 1024) return `${bytes} B`;
    if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
    return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
  }
}
