import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { EmailService } from "@bff/infra/email/email.service.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import type { SimNotificationContext } from "../../interfaces/sim-base.interface.js";

const ADMIN_EMAIL = "info@asolutions.co.jp";
const SIM_OPERATION_FAILURE_MESSAGE = "SIM operation failed. Please try again or contact support.";

/**
 * API call log structure for notification emails.
 * Payloads are serialized to JSON in email bodies via JSON.stringify.
 */
export interface ApiCallLog {
  url: string;
  senddata?: JsonObject | string;
  json?: JsonObject | string;
  result: JsonObject | string;
}

/** JSON-serializable object (used in API call logs for email notifications) */
type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
type JsonObject = { [key: string]: JsonValue | undefined };

/**
 * Unified SIM notification service.
 * Handles all SIM-related email notifications including:
 * - Internal action notifications (success/error alerts)
 * - API results notifications to admin
 * - Customer-facing emails (eSIM reissue, cancellation confirmations)
 */
@Injectable()
export class SimNotificationService {
  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly email: EmailService,
    private readonly configService: ConfigService
  ) {}

  // ============================================================================
  // Internal Action Notifications
  // ============================================================================

  /**
   * Send notification for SIM actions to configured alert email
   */
  async notifySimAction(
    action: string,
    status: "SUCCESS" | "ERROR",
    context: SimNotificationContext
  ): Promise<void> {
    const subject = `[SIM ACTION] ${action} - ${status}`;
    const toAddress = this.configService.get<string>("SIM_ALERT_EMAIL_TO");
    const fromAddress = this.configService.get<string>("SIM_ALERT_EMAIL_FROM");

    if (!toAddress || !fromAddress) {
      this.logger.debug("SIM action notification skipped: email config missing", {
        action,
        status,
      });
      return;
    }

    const publicContext = this.redactSensitiveFields(context);

    try {
      const lines: string[] = [
        `Action: ${action}`,
        `Result: ${status}`,
        `Timestamp: ${new Date().toISOString()}`,
        "",
        "Context:",
        JSON.stringify(publicContext, null, 2),
      ];
      await this.email.sendEmail({
        to: toAddress,
        from: fromAddress,
        subject,
        text: lines.join("\n"),
      });
    } catch (err) {
      this.logger.warn("Failed to send SIM action notification email", {
        action,
        status,
        error: extractErrorMessage(err),
      });
    }
  }

  // ============================================================================
  // API Results Notifications (Admin)
  // ============================================================================

  /**
   * Send API results notification email to admin
   */
  async sendApiResultsEmail(
    subject: string,
    apiCalls: ApiCallLog[],
    additionalInfo?: string
  ): Promise<void> {
    try {
      const lines: string[] = [];

      for (const call of apiCalls) {
        lines.push(`url: ${call.url}`);
        lines.push("");

        if (call.senddata) {
          const senddataStr =
            typeof call.senddata === "string"
              ? call.senddata
              : JSON.stringify(call.senddata, null, 2);
          lines.push(`senddata: ${senddataStr}`);
          lines.push("");
        }

        if (call.json) {
          const jsonStr =
            typeof call.json === "string" ? call.json : JSON.stringify(call.json, null, 2);
          lines.push(`json: ${jsonStr}`);
          lines.push("");
        }

        const resultStr =
          typeof call.result === "string" ? call.result : JSON.stringify(call.result, null, 2);
        lines.push(`result: ${resultStr}`);
        lines.push("");
        lines.push("---");
        lines.push("");
      }

      if (additionalInfo) {
        lines.push(additionalInfo);
      }

      await this.email.sendEmail({
        to: ADMIN_EMAIL,
        from: ADMIN_EMAIL,
        subject,
        text: lines.join("\n"),
      });

      this.logger.log("Sent API results notification email", {
        subject,
        to: ADMIN_EMAIL,
        callCount: apiCalls.length,
      });
    } catch (err) {
      this.logger.warn("Failed to send API results notification email", {
        subject,
        error: extractErrorMessage(err),
      });
    }
  }

  // ============================================================================
  // Customer Notifications
  // ============================================================================

  /**
   * Send customer notification email
   */
  async sendCustomerEmail(to: string, subject: string, body: string): Promise<void> {
    try {
      await this.email.sendEmail({
        to,
        from: ADMIN_EMAIL,
        subject,
        text: body,
      });

      this.logger.log("Sent customer notification email", {
        subject,
        to,
      });
    } catch (err) {
      this.logger.warn("Failed to send customer notification email", {
        subject,
        to,
        error: extractErrorMessage(err),
      });
    }
  }

  // ============================================================================
  // Email Body Builders
  // ============================================================================

  /**
   * Build eSIM reissue customer email body
   */
  buildEsimReissueEmail(customerName: string, simNumber: string, newEid: string): string {
    return `Dear ${customerName},

This is to confirm that your request to re-issue the SIM card ${simNumber}
to the EID=${newEid} has been accepted.

Please download the SIM plan, then follow the instructions to install the APN profile.

eSIM plan download: https://www.asolutions.co.jp/uploads/pdf/esim.pdf
APN profile instructions: https://www.asolutions.co.jp/sim-card/

With best regards,
Assist Solutions Customer Support
TEL: 0120-660-470 (Mon-Fri / 10AM-6PM)
Email: ${ADMIN_EMAIL}`;
  }

  /**
   * Build physical SIM reissue customer email body
   */
  buildPhysicalSimReissueEmail(customerName: string, simNumber: string): string {
    return `Dear ${customerName},

This is to confirm that your request to re-issue the SIM card ${simNumber}
as a physical SIM has been accepted.

You will be contacted by us again as soon as details about the shipping
schedule can be disclosed (typically in 3-5 business days).

With best regards,
Assist Solutions Customer Support
TEL: 0120-660-470 (Mon-Fri / 10AM-6PM)
Email: ${ADMIN_EMAIL}`;
  }

  /**
   * Build cancellation notification email body for admin
   */
  buildCancellationAdminEmail(params: {
    customerName: string;
    simNumber: string;
    serialNumber?: string;
    cancellationMonth: string;
    registeredEmail: string;
    comments?: string;
  }): string {
    return `The following SONIXNET SIM cancellation has been requested.

Customer name: ${params.customerName}
SIM #: ${params.simNumber}
Serial #: ${params.serialNumber || "N/A"}
Cancellation month: ${params.cancellationMonth}
Registered email address: ${params.registeredEmail}
Comments: ${params.comments || "N/A"}`;
  }

  // ============================================================================
  // Error Message Utilities
  // ============================================================================

  /**
   * Convert technical errors to user-friendly messages for SIM operations
   */
  getUserFriendlySimError(technicalError: string): string {
    if (!technicalError) {
      return SIM_OPERATION_FAILURE_MESSAGE;
    }

    const errorLower = technicalError.toLowerCase();

    // Freebit API errors
    if (errorLower.includes("api error: ng") || errorLower.includes("account not found")) {
      return "SIM account not found. Please contact support to verify your SIM configuration.";
    }

    if (errorLower.includes("authentication failed") || errorLower.includes("auth")) {
      return "SIM service is temporarily unavailable. Please try again later.";
    }

    if (errorLower.includes("timeout") || errorLower.includes("network")) {
      return "SIM service request timed out. Please try again.";
    }

    // WHMCS errors
    if (errorLower.includes("invalid permissions") || errorLower.includes("not allowed")) {
      return "SIM service is temporarily unavailable. Please contact support for assistance.";
    }

    // Generic errors
    if (errorLower.includes("failed") || errorLower.includes("error")) {
      return SIM_OPERATION_FAILURE_MESSAGE;
    }

    // Default fallback
    return SIM_OPERATION_FAILURE_MESSAGE;
  }

  // ============================================================================
  // Action Runner (for wrapping operations with notifications)
  // ============================================================================

  /**
   * Run an operation with automatic success/error notifications.
   * Replaces the separate SimActionRunnerService.
   */
  async runWithNotification<T>(
    action: string,
    options: {
      baseContext: SimNotificationContext;
      enrichSuccess?: (result: T) => Partial<SimNotificationContext>;
      enrichError?: (error: unknown) => Partial<SimNotificationContext>;
    },
    handler: () => Promise<T>
  ): Promise<T> {
    try {
      const result = await handler();
      const successContext = {
        ...options.baseContext,
        ...(options.enrichSuccess ? options.enrichSuccess(result) : {}),
      };
      await this.notifySimAction(action, "SUCCESS", successContext);
      return result;
    } catch (error) {
      const errorContext = {
        ...options.baseContext,
        error: extractErrorMessage(error),
        ...(options.enrichError ? options.enrichError(error) : {}),
      };
      await this.notifySimAction(action, "ERROR", errorContext);
      throw error;
    }
  }

  // ============================================================================
  // Private Helpers
  // ============================================================================

  /**
   * Redact sensitive information from notification context
   */
  private redactSensitiveFields(context: SimNotificationContext): SimNotificationContext {
    const sanitized: SimNotificationContext = {};
    for (const [key, value] of Object.entries(context)) {
      if (typeof key === "string" && key.toLowerCase().includes("password")) {
        sanitized[key] = "[REDACTED]";
        continue;
      }

      if (typeof value === "string" && value.length > 200) {
        sanitized[key] = `${value.slice(0, 200)}…`;
        continue;
      }

      sanitized[key] = value;
    }
    return sanitized;
  }
}
