import { Injectable, Inject, BadRequestException } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { FreebitOrchestratorService } from "@bff/integrations/freebit/services/freebit-orchestrator.service";
import { MappingsService } from "@bff/modules/id-mappings/mappings.service";
import { SimValidationService } from "./sim-validation.service";
import { getErrorMessage } from "@bff/core/utils/error.util";
import type { SimTopUpRequest } from "@customer-portal/domain/sim";
import { SimBillingService } from "./sim-billing.service";
import { SimActionRunnerService } from "./sim-action-runner.service";
import { SimApiNotificationService } from "./sim-api-notification.service";

@Injectable()
export class SimTopUpService {
  constructor(
    private readonly freebitService: FreebitOrchestratorService,
    private readonly mappingsService: MappingsService,
    private readonly simValidation: SimValidationService,
    private readonly simBilling: SimBillingService,
    private readonly simActionRunner: SimActionRunnerService,
    private readonly apiNotification: SimApiNotificationService,
    private readonly configService: ConfigService,
    @Inject(Logger) private readonly logger: Logger
  ) {}

  private get whmcsBaseUrl(): string {
    return this.configService.get<string>("WHMCS_BASE_URL") || "https://accounts.asolutions.co.jp/includes/api.php";
  }

  private get freebitBaseUrl(): string {
    return this.configService.get<string>("FREEBIT_BASE_URL") || "https://i1.mvno.net/emptool/api";
  }

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

    await this.simActionRunner.run(
      "Top Up Data",
      {
        baseContext: {
          userId,
          subscriptionId,
          quotaMb: request.quotaMb,
        },
        enrichSuccess: meta => ({
          account: meta.account,
          costJpy: meta.costJpy,
          invoiceId: meta.invoiceId,
          transactionId: meta.transactionId,
        }),
        enrichError: () => ({
          account: latestAccount,
        }),
      },
      async () => {
        const validation = await this.simValidation.validateSimSubscription(userId, subscriptionId);
        latestAccount = validation.account;

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

        const quotaGb = request.quotaMb / 1000;
        const units = Math.ceil(quotaGb);
        const costJpy = units * 500;

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

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

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

        const billing = await this.simBilling.createOneTimeCharge({
          clientId: mapping.whmcsClientId,
          description: `SIM Data Top-up: ${units}GB for ${latestAccount}`,
          amountJpy: costJpy,
          currency: "JPY",
          dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
          notes: `Subscription ID: ${subscriptionId}, Phone: ${latestAccount}`,
          failureNotesPrefix: "Payment capture failed",
          publicErrorMessage: "SIM top-up failed: payment could not be processed",
          metadata: { subscriptionId },
        });

        // Call Freebit API to add quota
        let freebitResult: { resultCode: string; status: { message: string; statusCode: string } } | null = null;
        try {
          await this.freebitService.topUpSim(latestAccount, request.quotaMb, {});
          freebitResult = { resultCode: "100", status: { message: "OK", statusCode: "200" } };
        } catch (freebitError) {
          await this.handleFreebitFailureAfterPayment(
            freebitError,
            billing.invoice,
            billing.transactionId || "unknown",
            userId,
            subscriptionId,
            latestAccount,
            request.quotaMb
          );
        }

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

        // Send API results email notification
        const today = new Date();
        const dateStr = today.toISOString().split("T")[0];
        const dueDate = new Date(today.getTime() + 2 * 24 * 60 * 60 * 1000).toISOString().split("T")[0];

        await this.apiNotification.sendApiResultsEmail(
          "API results",
          [
            {
              url: this.whmcsBaseUrl,
              senddata: {
                itemdescription1: `Top-up data (${units}GB)\nSIM Number: ${latestAccount}`,
                itemamount1: String(costJpy),
                userid: String(mapping.whmcsClientId),
                date: dateStr,
                responsetype: "json",
                itemtaxed1: "1",
                action: "CreateInvoice",
                duedate: dueDate,
                paymentmethod: "stripe",
                sendinvoice: "1",
              },
              result: { result: "success", invoiceid: billing.invoice.id, status: billing.invoice.status },
            },
            {
              url: this.whmcsBaseUrl,
              senddata: {
                responsetype: "json",
                action: "CapturePayment",
                invoiceid: billing.invoice.id,
              },
              result: { result: "success" },
            },
            {
              url: `${this.freebitBaseUrl}/master/addSpec/`,
              json: {
                quota: request.quotaMb,
                kind: "MVNO",
                account: latestAccount,
                authKey: "[REDACTED]",
              },
              result: freebitResult || { resultCode: "100", status: { message: "OK", statusCode: "200" } },
            },
          ]
        );

        return {
          account: latestAccount,
          costJpy,
          invoiceId: billing.invoice.id,
          transactionId: billing.transactionId,
        };
      }
    );
  }

  /**
   * 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.simBilling.appendInvoiceNote(
        invoice.id,
        `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),
      });
    }

    // Refund logic is handled by the caller (via top-up failure handling)
    // Automatic refunds should be implemented at the payment processing layer
    // to ensure consistency across all failure scenarios.
    // For manual refunds, use the WHMCS admin panel or dedicated refund endpoints.

    throw new Error(
      `Payment was processed but SIM data top-up failed. Please contact support with invoice ${invoice.number} for assistance.`
    );
  }
}
