import { Injectable, Logger, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis';
import { ReferralRelationshipRepository } from '../../infrastructure/repositories/referral-relationship.repository';
import { ReferralRewardRepository } from '../../infrastructure/repositories/referral-reward.repository';
import { ReferralStatRepository } from '../../infrastructure/repositories/referral-stat.repository';
import { ProcessedEventRepository } from '../../infrastructure/repositories/processed-event.repository';

interface PaymentReceivedPayload {
  tenantId: string;
  invoiceId: string;
  paymentId: string;
  amount: number;       // USD cents
  currency: string;
  provider: string;
  planName?: string;    // 'free' | 'pro' | 'enterprise'
}

// Reward rules (USD cents)
const FIRST_PAYMENT_REWARDS: Record<string, { referrer: number; referred: number }> = {
  pro:        { referrer: 1500, referred: 500  }, // $15 / $5
  enterprise: { referrer: 5000, referred: 2000 }, // $50 / $20
};
const RECURRING_PERCENTAGE = 10;   // 10% of payment amount
const RECURRING_MAX_MONTHS  = 12;
const LEVEL2_PERCENTAGE     = 5;   // 5% for level-2
const LEVEL2_MAX_MONTHS     = 6;

const STREAM_KEY     = 'events:payment.received';
const CONSUMER_GROUP = 'referral-service';
const CONSUMER_NAME  = 'referral-consumer-1';

@Injectable()
export class ConsumePaymentReceivedUseCase implements OnModuleInit, OnModuleDestroy {
  private readonly logger = new Logger(ConsumePaymentReceivedUseCase.name);
  private client: Redis;
  private running = false;

  constructor(
    private readonly relationshipRepo: ReferralRelationshipRepository,
    private readonly rewardRepo: ReferralRewardRepository,
    private readonly statRepo: ReferralStatRepository,
    private readonly processedRepo: ProcessedEventRepository,
    private readonly configService: ConfigService,
  ) {}

  async onModuleInit() {
    const redisUrl = this.configService.get<string>('REDIS_URL', 'redis://localhost:6379');
    this.client = new Redis(redisUrl);

    try {
      await this.client.xgroup('CREATE', STREAM_KEY, CONSUMER_GROUP, '0', 'MKSTREAM');
    } catch {
      // Group already exists
    }

    this.running = true;
    this.consumeLoop().catch((err) =>
      this.logger.error(`Payment consumer loop error: ${err.message}`),
    );
  }

  async onModuleDestroy() {
    this.running = false;
    this.client.disconnect();
  }

  private async consumeLoop() {
    while (this.running) {
      try {
        const response = await this.client.xreadgroup(
          'GROUP', CONSUMER_GROUP, CONSUMER_NAME,
          'COUNT', '10',
          'BLOCK', '5000',
          'STREAMS', STREAM_KEY, '>',
        ) as Array<[string, Array<[string, string[]]>]> | null;

        if (!response) continue;

        for (const [, messages] of response) {
          for (const [id, fields] of messages) {
            const record: Record<string, string> = {};
            for (let i = 0; i < fields.length; i += 2) {
              record[fields[i]] = fields[i + 1];
            }
            await this.processMessage(id, record);
            await this.client.xack(STREAM_KEY, CONSUMER_GROUP, id);
          }
        }
      } catch (err) {
        if (this.running) {
          this.logger.error(`Redis consumer error: ${err.message}`);
          await new Promise((r) => setTimeout(r, 5000));
        }
      }
    }
  }

  private async processMessage(msgId: string, record: Record<string, string>) {
    try {
      const payload: PaymentReceivedPayload = JSON.parse(record['data'] ?? '{}');
      if (!payload.tenantId || !payload.invoiceId) return;

      // Idempotency: skip if already processed
      const eventId = `payment:${payload.paymentId ?? msgId}`;
      if (await this.processedRepo.hasProcessed(eventId)) {
        this.logger.debug(`Skipping already-processed payment: ${eventId}`);
        return;
      }

      const { tenantId, invoiceId, amount, planName } = payload;

      // Find referral relationships where this tenant is the referred party
      const level1 = await this.relationshipRepo.findByReferredTenantId(tenantId);
      if (!level1) {
        // No referral relationship — nothing to do
        await this.processedRepo.markProcessed(eventId, 'payment.received');
        return;
      }

      const isFirstPayment = level1.status === 'PENDING';

      if (isFirstPayment) {
        // Mark relationship as ACTIVE
        await this.relationshipRepo.updateStatus(level1.id, 'ACTIVE', {
          activatedAt: new Date(),
        });
        await this.statRepo.incrementActiveCount(level1.referrerTenantId);

        // Issue first-payment rewards based on plan
        const plan = planName?.toLowerCase() ?? 'pro';
        const rewardRules = FIRST_PAYMENT_REWARDS[plan] ?? FIRST_PAYMENT_REWARDS['pro'];

        // Reward to referrer
        await this.issueCredit(
          level1.referrerTenantId,
          level1.id,
          rewardRules.referrer,
          'FIRST_PAYMENT',
          invoiceId,
        );

        // Bonus to referred tenant (welcome credit)
        await this.issueCredit(
          tenantId,
          level1.id,
          rewardRules.referred,
          'FIRST_PAYMENT',
          invoiceId,
        );

        // Mark relationship as REWARDED (first-payment reward done)
        await this.relationshipRepo.updateStatus(level1.id, 'REWARDED', {
          rewardedAt: new Date(),
        });

        this.logger.log(
          `First-payment rewards issued: referrer=${level1.referrerTenantId} (+$${rewardRules.referrer / 100}), referred=${tenantId} (+$${rewardRules.referred / 100})`,
        );
      } else if (level1.status === 'ACTIVE' || level1.status === 'REWARDED') {
        // Recurring reward: 10% of payment for up to 12 months
        const recurringCount = await this.rewardRepo.countRecurringByRelationship(level1.id);
        if (recurringCount < RECURRING_MAX_MONTHS) {
          const recurringCents = Math.floor(amount * RECURRING_PERCENTAGE / 100);
          if (recurringCents > 0) {
            await this.issueCredit(
              level1.referrerTenantId,
              level1.id,
              recurringCents,
              'RECURRING',
              invoiceId,
              recurringCount + 1,
            );
            this.logger.log(
              `Recurring reward issued: referrer=${level1.referrerTenantId} +${recurringCents} cents (month ${recurringCount + 1}/${RECURRING_MAX_MONTHS})`,
            );
          }
        }
      }

      await this.processedRepo.markProcessed(eventId, 'payment.received');
    } catch (err) {
      this.logger.error(`Failed to process payment message ${msgId}: ${err.message}`);
    }
  }

  private async issueCredit(
    beneficiaryTenantId: string,
    referralRelationshipId: string,
    amountCents: number,
    triggerType: 'FIRST_PAYMENT' | 'RECURRING',
    sourceInvoiceId: string,
    recurringMonth?: number,
  ) {
    const reward = await this.rewardRepo.create({
      beneficiaryTenantId,
      referralRelationshipId,
      rewardType: 'CREDIT',
      triggerType,
      amountCents,
      sourceInvoiceId,
      recurringMonth,
    });

    // Update stats
    await this.statRepo.upsert(beneficiaryTenantId);
    await this.statRepo.addCreditEarned(beneficiaryTenantId, amountCents);

    return reward;
  }
}
