import { BadRequestException, Inject, Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { Logger } from "nestjs-pino";
import { WhmcsInvoiceService } from "@bff/integrations/whmcs/services/whmcs-invoice.service.js";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";

export interface SimChargeInvoiceResult {
  invoice: { id: number; number: string; total: number; status: string };
  transactionId?: string;
  refunded?: boolean;
}

interface OneTimeChargeParams {
  clientId: number;
  userId?: string;
  description: string;
  amountJpy: number;
  currency?: string;
  dueDate?: Date;
  notes?: string;
  failureNotesPrefix?: string;
  publicErrorMessage?: string;
  metadata?: Record<string, unknown>;
}

@Injectable()
export class SimBillingService {
  private readonly testMode: boolean;

  constructor(
    private readonly whmcsInvoiceService: WhmcsInvoiceService,
    private readonly configService: ConfigService,
    @Inject(Logger) private readonly logger: Logger
  ) {
    // Enable test mode via environment variable
    this.testMode = this.configService.get<string>("SIM_BILLING_TEST_MODE") === "true";
    if (this.testMode) {
      this.logger.warn("SIM Billing is in TEST MODE - payments will be automatically refunded");
    }
  }

  async createOneTimeCharge(params: OneTimeChargeParams): Promise<SimChargeInvoiceResult> {
    const {
      clientId,
      userId,
      description,
      amountJpy,
      currency = "JPY",
      dueDate,
      notes,
      failureNotesPrefix = "Payment failed",
      publicErrorMessage,
      metadata = {},
    } = params;

    const invoice = await this.whmcsInvoiceService.createInvoice({
      clientId,
      description,
      amount: amountJpy,
      currency,
      ...(dueDate && { dueDate }),
      ...(notes && { notes }),
    });

    const paymentResult = await this.whmcsInvoiceService.capturePayment({
      invoiceId: invoice.id,
      amount: amountJpy,
      currency,
      ...(userId && { userId }),
    });

    if (!paymentResult.success) {
      const failureMessage = `${failureNotesPrefix}: ${paymentResult.error || "unknown error"}`;
      await this.cancelInvoice(invoice.id, failureMessage);

      this.logger.error("SIM billing payment failed", {
        invoiceId: invoice.id,
        clientId,
        description,
        amountJpy,
        ...metadata,
        error: paymentResult.error,
      });

      throw new BadRequestException(publicErrorMessage ?? failureMessage);
    }

    this.logger.log("SIM billing payment captured", {
      invoiceId: invoice.id,
      clientId,
      description,
      amountJpy,
      transactionId: paymentResult.transactionId,
      testMode: this.testMode,
      ...metadata,
    });

    // In test mode, automatically refund the payment
    let refunded = false;
    if (this.testMode && paymentResult.transactionId) {
      this.logger.log("TEST MODE: Automatically refunding payment", {
        invoiceId: invoice.id,
        transactionId: paymentResult.transactionId,
        amount: amountJpy,
      });

      const refundResult = await this.whmcsInvoiceService.refundPayment({
        invoiceId: invoice.id,
        transactionId: paymentResult.transactionId,
        amount: amountJpy,
        reason: "TEST MODE - Automatic refund for SIM billing test",
      });

      if (refundResult.success) {
        refunded = true;
        this.logger.log("TEST MODE: Payment refunded successfully", {
          invoiceId: invoice.id,
          amount: amountJpy,
        });
      } else {
        this.logger.warn("TEST MODE: Failed to refund payment", {
          invoiceId: invoice.id,
          error: refundResult.error,
        });
      }
    }

    return {
      invoice,
      ...(paymentResult.transactionId && { transactionId: paymentResult.transactionId }),
      refunded,
    };
  }

  async appendInvoiceNote(invoiceId: number, note: string): Promise<void> {
    await this.whmcsInvoiceService.updateInvoice({
      invoiceId,
      notes: note,
    });
  }

  private async cancelInvoice(invoiceId: number, reason: string): Promise<void> {
    try {
      await this.whmcsInvoiceService.updateInvoice({
        invoiceId,
        status: "Cancelled",
        notes: reason,
      });
    } catch (error) {
      this.logger.error("Failed to cancel invoice after payment failure", {
        invoiceId,
        error: extractErrorMessage(error),
      });
    }
  }
}
