import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { PrismaService } from '../../prisma/prisma.service';
import { PaymentFactory } from '../payments/payment.factory';
import { BusinessService } from '../business/business.service';
import { OrdersService } from '../commerce/services/orders.service';

export interface PollingJob {
  paymentReference: string;
  orderId: string;
  businessId: string;
  whatsappNumber: string;
  retries?: number;
}

/**
 * Polls PesePay for payment status after a seamless EcoCash payment is initiated.
 * Mirrors the checkPaymentStatusPeriodically logic from wa-shop/server.js.
 * Sends WhatsApp notifications on success / failure.
 */
@Injectable()
export class PaymentPollingService implements OnModuleInit {
  private readonly logger = new Logger(PaymentPollingService.name);
  // Max polling attempts (6 × 15s = 90 seconds total)
  private readonly MAX_RETRIES = 6;
  private readonly POLL_INTERVAL_MS = 15_000;

  // Lazily injected WhatsApp service to avoid circular dependency
  private whatsappServiceRef: { sendTextNotification(businessId: string, toPhone: string, message: string): Promise<void> } | null = null;
  private ordersService!: OrdersService;

  constructor(
    private readonly prisma: PrismaService,
    private readonly paymentFactory: PaymentFactory,
    private readonly businessService: BusinessService,
    private readonly moduleRef: ModuleRef,
  ) {}

  onModuleInit() {
    this.ordersService = this.moduleRef.get(OrdersService, { strict: false });
  }

  /**
   * Allows WhatsApp module to register itself at runtime.
   * This avoids a circular module dependency.
   */
  registerWhatsAppService(svc: { sendTextNotification(businessId: string, toPhone: string, message: string): Promise<void> }) {
    this.whatsappServiceRef = svc;
  }

  /**
   * Kicks off background polling for a payment reference.
   * Returns immediately — polling happens asynchronously.
   */
  startPolling(job: PollingJob): void {
    const retries = job.retries ?? this.MAX_RETRIES;
    this.logger.log(`Starting payment poll: ref=${job.paymentReference}, orderId=${job.orderId}, attempts=${retries}`);
    // Delay first check slightly so PesePay has time to process
    setTimeout(() => this.poll(job, retries), 3_000);
  }

  private async poll(job: PollingJob, retries: number): Promise<void> {
    if (retries <= 0) {
      this.logger.warn(`Polling timeout for ref=${job.paymentReference}. Marking as PAYMENT_TIMEOUT.`);
      await this.updateOrderStatus(job.orderId, 'PAYMENT_TIMEOUT', 'FAILED');
      await this.clearCustomerSessionIfAwaitingPayment(job.businessId, job.whatsappNumber);
      
      const order = await this.prisma.order.findUnique({
        where: { id: job.orderId },
      });
      if (order) {
        await this.notifyOwner(job.businessId, order.orderNumber, order.customerName, order.customerPhone, order.totalAmount, order.currency, 'UNPAID');
      }

      await this.notify(
        job.businessId,
        job.whatsappNumber,
        `We could not verify your EcoCash payment automatically.\n\nReference: *${job.paymentReference}*\n\nPlease contact support if payment was deducted. Reply *menu* to continue shopping.`,
      );
      return;
    }

    try {
      this.logger.log(`Polling payment status: ref=${job.paymentReference}, attempts_left=${retries}`);

      const { gateway } = await this.resolveGateway(job.orderId, job.businessId);
      const result = await gateway.verifyPayment(job.paymentReference);
      const status = result.status;

      this.logger.log(`Poll result: ref=${job.paymentReference}, status=${status}`);

      if (status === 'SUCCESS') {
        await this.ordersService.confirmPayment(job.paymentReference, 'SUCCESS', result.gatewayReference);
        return;
      }

      if (status === 'FAILED') {
        await this.ordersService.confirmPayment(job.paymentReference, 'FAILED', result.gatewayReference);
        return;
      }

      // PENDING — keep polling
      this.logger.log(`Payment still pending for ref=${job.paymentReference}, retrying in ${this.POLL_INTERVAL_MS / 1000}s`);
      setTimeout(() => this.poll(job, retries - 1), this.POLL_INTERVAL_MS);

    } catch (err: any) {
      this.logger.error(`Poll error for ref=${job.paymentReference}: ${err.message}`);
      if (retries > 1) {
        setTimeout(() => this.poll(job, retries - 1), this.POLL_INTERVAL_MS);
      } else {
        await this.updateOrderStatus(job.orderId, 'PAYMENT_TIMEOUT', 'FAILED');
      }
    }
  }

  private async resolveGateway(orderId: string, businessId: string) {
    const payment = await this.prisma.payment.findFirst({
      where: { orderId },
      orderBy: { createdAt: 'desc' },
    });

    const gatewayType = payment?.gateway || 'mock';
    let gatewayCreds: Record<string, any> = { isSandbox: true };

    if (gatewayType !== 'mock') {
      gatewayCreds = (await this.businessService.getDecryptedCredentials(businessId, gatewayType)) || {};
    }

    const gateway = this.paymentFactory.getGateway(gatewayType, gatewayCreds);
    return { gateway, gatewayType };
  }

  private async updateOrderStatus(orderId: string, status: string, paymentStatus: string) {
    try {
      await this.prisma.order.update({
        where: { id: orderId },
        data: { status, paymentStatus },
      });
    } catch (err: any) {
      this.logger.error(`Failed to update order ${orderId} status: ${err.message}`);
    }
  }

  private async updatePaymentRecord(paymentReference: string, status: string, gatewayReference?: string) {
    try {
      await this.prisma.payment.updateMany({
        where: { paymentReference },
        data: {
          status,
          ...(gatewayReference ? { gatewayReference } : {}),
        },
      });
    } catch (err: any) {
      this.logger.error(`Failed to update payment record ${paymentReference}: ${err.message}`);
    }
  }

  async notify(businessId: string, whatsappNumber: string, message: string) {
    if (!this.whatsappServiceRef) {
      this.logger.warn(`WhatsApp service not registered — cannot send notification to ${whatsappNumber}`);
      return;
    }
    try {
      await this.whatsappServiceRef.sendTextNotification(businessId, whatsappNumber, message);
    } catch (err: any) {
      this.logger.error(`Failed to send WhatsApp notification to ${whatsappNumber}: ${err.message}`);
    }
  }

  async notifyOwner(
    businessId: string,
    orderNumber: string,
    customerName: string,
    customerPhone: string,
    totalAmount: number,
    currency: string,
    status: 'DONE' | 'UNPAID',
  ) {
    let ownerPhone: string | undefined | null = null;
    try {
      const business = await this.prisma.business.findUnique({
        where: { id: businessId },
      });
      ownerPhone = business?.contactPhone;
    } catch (err: any) {
      this.logger.error(`Error fetching business contact phone: ${err.message}`);
    }

    if (!ownerPhone) {
      ownerPhone = process.env.SELLER_WHATSAPP_NUMBER;
    }

    if (!ownerPhone) {
      this.logger.warn(`No seller phone number found to notify for business ${businessId}`);
      return;
    }

    let cleanPhone = ownerPhone.trim().replace(/[+\s]+/g, '');
    if (/^0[0-9]{9}$/.test(cleanPhone)) {
      cleanPhone = '263' + cleanPhone.substring(1);
    }

    const message = `🔔 *Store Owner Notification*\n\n` +
      `Order: *${orderNumber}*\n` +
      `Customer: *${customerName}* (${customerPhone})\n` +
      `Total: *${currency} $${totalAmount.toFixed(2)}*\n` +
      `Payment Status: *${status}*`;

    try {
      await this.notify(businessId, cleanPhone, message);
    } catch (err: any) {
      this.logger.error(`Failed to notify store owner ${cleanPhone}: ${err.message}`);
    }
  }

  private async clearCustomerSessionIfAwaitingPayment(businessId: string, whatsappNumber: string) {
    try {
      const session = await this.prisma.session.findFirst({
        where: { businessId, whatsappNumber },
      });
      if (session && session.currentStep === 'AWAITING_PAYMENT') {
        await this.prisma.session.update({
          where: { id: session.id },
          data: {
            currentStep: 'WELCOME',
            sessionDataJson: '{}',
          },
        });
        this.logger.log(`Cleared AWAITING_PAYMENT session for customer ${whatsappNumber}`);
      }
    } catch (err: any) {
      this.logger.error(`Failed to clear customer session: ${err.message}`);
    }
  }
}
