import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { FreebitOrchestratorService } from "@bff/integrations/freebit/services/freebit-orchestrator.service";
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { SimValidationService } from "./sim-validation.service";
import { SimNotificationService } from "./sim-notification.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type { SimTopUpRequest } from "../types/sim-requests.types";

@Injectable()
export class SimTopUpService {
  constructor(
    private readonly freebitService: FreebitOrchestratorService,
    private readonly whmcsService: WhmcsService,
    private readonly mappingsService: MappingsService,
    private readonly simValidation: SimValidationService,
    private readonly simNotification: SimNotificationService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  /**
   * Top up SIM data quota with payment processing
   * Pricing: 1GB = 500 JPY
   */
  async topUpSim(userId: string, subscriptionId: number, request: SimTopUpRequest): Promise<void> {
    let account: string = "";
    let costJpy = 0;
    let currency = request.currency ?? "JPY";

    try {
      const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
      account = validation.account;

      // Validate quota amount
      if (request.quotaMb <= 0 || request.quotaMb > 100000) {
        throw new BadRequestException("Quota must be between 1MB and 100GB");
      }

      // Use amount from request (calculated by frontend)
      const quotaGb = request.quotaMb / 1000;
      const units = Math.ceil(quotaGb);
      const expectedCost = units * 500;

      costJpy = request.amount ?? expectedCost;
      currency = request.currency ?? "JPY";

      if (request.amount != null && request.amount !== expectedCost) {
        throw new BadRequestException(
          `Amount mismatch: expected ¥${expectedCost} for ${units}GB, got ¥${request.amount}`
        );
      }

      // Validate quota against Freebit API limits (100MB - 51200MB)
      if (request.quotaMb < 100 || request.quotaMb > 51200) {
        throw new BadRequestException(
          "Quota must be between 100MB and 51200MB (50GB) for Freebit API compatibility"
        );
      }

      // Get client mapping for WHMCS
      const mapping = await this.mappingsService.findByUserId(userId);
      if (!mapping?.whmcsClientId) {
        throw new BadRequestException("WHMCS client mapping not found");
      }

      const whmcsClientId = mapping.whmcsClientId;

      this.logger.log(`Starting SIM top-up process for subscription ${subscriptionId}`, {
        userId,
        subscriptionId,
        account,
        quotaMb: request.quotaMb,
        quotaGb: quotaGb.toFixed(2),
        costJpy,
        currency,
      });

      // Step 1: Create WHMCS invoice
      const invoice = await this.whmcsService.createInvoice({
        clientId: whmcsClientId,
        description: `SIM Data Top-up: ${units}GB for ${account}`,
        amount: costJpy,
        currency,
        dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days from now
        notes: `Subscription ID: ${subscriptionId}, Phone: ${account}`,
      });

      this.logger.log(`Created WHMCS invoice ${invoice.id} for SIM top-up`, {
        invoiceId: invoice.id,
        invoiceNumber: invoice.number,
        amount: costJpy,
        currency,
        subscriptionId,
      });

      // Step 2: Capture payment
      this.logger.log(`Attempting payment capture`, {
        invoiceId: invoice.id,
        amount: costJpy,
        currency,
      });

      const paymentResult = await this.whmcsService.capturePayment({
        invoiceId: invoice.id,
        amount: costJpy,
        currency,
      });

      if (!paymentResult.success) {
        this.logger.error(`Payment capture failed for invoice ${invoice.id}`, {
          invoiceId: invoice.id,
          error: paymentResult.error,
          subscriptionId,
        });

        // Cancel the invoice since payment failed
        await this.handlePaymentFailure(invoice.id, paymentResult.error || "Unknown payment error");

        throw new BadRequestException(`SIM top-up failed: ${paymentResult.error}`);
      }

      this.logger.log(`Payment captured successfully for invoice ${invoice.id}`, {
        invoiceId: invoice.id,
        transactionId: paymentResult.transactionId,
        amount: costJpy,
        currency,
        subscriptionId,
      });

      try {
        // Step 3: Only if payment successful, add data via Freebit
        await this.freebitService.topUpSim(account, request.quotaMb, {});

        this.logger.log(`Successfully topped up SIM for subscription ${subscriptionId}`, {
          userId,
          subscriptionId,
          account,
          quotaMb: request.quotaMb,
          costJpy,
          currency,
          invoiceId: invoice.id,
          transactionId: paymentResult.transactionId,
        });

        await this.simNotification.notifySimAction("Top Up Data", "SUCCESS", {
          userId,
          subscriptionId,
          account,
          quotaMb: request.quotaMb,
          costJpy,
          currency,
          invoiceId: invoice.id,
          transactionId: paymentResult.transactionId,
        });
      } catch (freebitError) {
        // If Freebit fails after payment, handle carefully
        await this.handleFreebitFailureAfterPayment(
          freebitError,
          invoice,
          paymentResult.transactionId || "unknown",
          userId,
          subscriptionId,
          account,
          request.quotaMb
        );
      }
    } catch (error) {
      const sanitizedError = getErrorMessage(error);
      this.logger.error(`Failed to top up SIM for subscription ${subscriptionId}`, {
        error: sanitizedError,
        userId,
        subscriptionId,
        quotaMb: request.quotaMb,
        costJpy,
        currency,
      });
      await this.simNotification.notifySimAction("Top Up Data", "ERROR", {
        userId,
        subscriptionId,
        account: account ?? "",
        quotaMb: request.quotaMb,
        costJpy,
        currency,
        error: sanitizedError,
      });
      throw error;
    }
  }

  /**
   * Handle payment failure by canceling the invoice
   */
  private async handlePaymentFailure(invoiceId: number, error: string): Promise<void> {
    try {
      await this.whmcsService.updateInvoice({
        invoiceId,
        status: "Cancelled",
        notes: `Payment capture failed: ${error}. Invoice cancelled automatically.`,
      });

      this.logger.log(`Cancelled invoice ${invoiceId} due to payment failure`, {
        invoiceId,
        reason: "Payment capture failed",
      });
    } catch (cancelError) {
      this.logger.error(`Failed to cancel invoice ${invoiceId} after payment failure`, {
        invoiceId,
        cancelError: getErrorMessage(cancelError),
        originalError: error,
      });
    }
  }

  /**
   * Handle Freebit API failure after successful payment
   */
  private async handleFreebitFailureAfterPayment(
    freebitError: unknown,
    invoice: { id: number; number: string },
    transactionId: string,
    userId: string,
    subscriptionId: number,
    account: string,
    quotaMb: number
  ): Promise<void> {
    this.logger.error(
      `Freebit API failed after successful payment for subscription ${subscriptionId}`,
      {
        error: getErrorMessage(freebitError),
        userId,
        subscriptionId,
        account,
        quotaMb,
        invoiceId: invoice.id,
        transactionId,
        paymentCaptured: true,
      }
    );

    // Add a note to the invoice about the Freebit failure
    try {
      await this.whmcsService.updateInvoice({
        invoiceId: invoice.id,
        notes: `Payment successful but SIM top-up failed: ${getErrorMessage(freebitError)}. Manual intervention required.`,
      });

      this.logger.log(`Added failure note to invoice ${invoice.id}`, {
        invoiceId: invoice.id,
        reason: "Freebit API failure after payment",
      });
    } catch (updateError) {
      this.logger.error(`Failed to update invoice ${invoice.id} with failure note`, {
        invoiceId: invoice.id,
        updateError: getErrorMessage(updateError),
        originalError: getErrorMessage(freebitError),
      });
    }

    // TODO: Implement refund logic here
    // await this.whmcsService.addCredit({
    //   clientId: whmcsClientId,
    //   description: `Refund for failed SIM top-up (Invoice: ${invoice.number})`,
    //   amount: costJpy,
    //   type: 'refund'
    // });

    const errMsg = `Payment was processed but SIM data top-up failed. Please contact support with invoice ${invoice.number} for assistance.`;
    await this.simNotification.notifySimAction("Top Up Data", "ERROR", {
      userId,
      subscriptionId,
      account,
      quotaMb,
      invoiceId: invoice.id,
      transactionId,
      error: getErrorMessage(freebitError),
    });
    throw new Error(errMsg);
  }
}
