import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
import { PlanRepository } from '../../infrastructure/repositories/plan.repository';
import { SubscriptionRepository } from '../../infrastructure/repositories/subscription.repository';
import { InvoiceRepository } from '../../infrastructure/repositories/invoice.repository';
import { SubscriptionLifecycleService } from '../../domain/services/subscription-lifecycle.service';
import { InvoiceGeneratorService } from '../../domain/services/invoice-generator.service';
import { InvoiceCurrency } from '../../domain/entities/invoice.entity';

export interface ChangePlanDto {
  tenantId: string;
  newPlanName: string;
  currency?: InvoiceCurrency;
}

@Injectable()
export class ChangePlanUseCase {
  private readonly logger = new Logger(ChangePlanUseCase.name);

  constructor(
    private readonly planRepo: PlanRepository,
    private readonly subscriptionRepo: SubscriptionRepository,
    private readonly invoiceRepo: InvoiceRepository,
    private readonly lifecycle: SubscriptionLifecycleService,
    private readonly invoiceGenerator: InvoiceGeneratorService,
  ) {}

  async execute(dto: ChangePlanDto): Promise<void> {
    const { tenantId, newPlanName, currency = InvoiceCurrency.USD } = dto;

    const subscription = await this.subscriptionRepo.findByTenantId(tenantId);
    if (!subscription) throw new NotFoundException(`No subscription found for tenant ${tenantId}`);

    const currentPlan = await this.planRepo.findById(subscription.planId);
    if (!currentPlan) throw new NotFoundException('Current plan not found');

    const newPlan = await this.planRepo.findByName(newPlanName);
    if (!newPlan) throw new NotFoundException(`Plan '${newPlanName}' not found`);

    if (currentPlan.id === newPlan.id) {
      throw new BadRequestException('Already on this plan');
    }

    const isUpgrade = newPlan.monthlyPriceCentsUsd > currentPlan.monthlyPriceCentsUsd;

    if (isUpgrade) {
      // Immediate upgrade with proration invoice item
      const updated = this.lifecycle.upgradeNow(subscription, newPlan.id);
      await this.subscriptionRepo.save(updated);

      // Generate proration charge if not on free plan
      if (newPlan.monthlyPriceCentsUsd > 0 && currentPlan.monthlyPriceCentsUsd >= 0) {
        const invoiceNumber = await this.invoiceRepo.getNextInvoiceNumber();
        const proratedItem = this.invoiceGenerator.generateProratedUpgradeItem(
          currentPlan,
          newPlan,
          new Date(),
          subscription.currentPeriodEnd,
          currency,
        );
        if (proratedItem.amount > 0) {
          const proInvoice = {
            tenantId,
            subscriptionId: subscription.id,
            invoiceNumber,
            currency,
            periodStart: new Date(),
            periodEnd: subscription.currentPeriodEnd,
            dueDate: new Date(),
            subtotalCents: proratedItem.amount,
            taxCents: 0,
            totalCents: proratedItem.amount,
            amountDueCents: proratedItem.amount,
          } as any;
          await this.invoiceRepo.saveWithItems(proInvoice, [proratedItem]);
        }
      }

      this.logger.log(`Tenant ${tenantId} upgraded to ${newPlanName}`);
    } else {
      // Downgrade: schedule for next period
      const updated = this.lifecycle.scheduleDowngrade(subscription, newPlan.id);
      await this.subscriptionRepo.save(updated);
      this.logger.log(`Tenant ${tenantId} scheduled downgrade to ${newPlanName} at period end`);
    }
  }
}
