import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, EventBus, type ICommandHandler } from '@nestjs/cqrs';
import { DomainException, NotFoundException, ValidationException, CacheService, CachePrefix, PrismaService, LoggerService } from '@modules/shared';
import {
  SUBSCRIPTION_REPOSITORY,
  type ISubscriptionRepository,
} from '../../../domain/repositories/subscription.repository';
import { UpgradeSubscriptionCommand } from './upgrade-subscription.command';

const TIER_ORDER = { FREE: 0, AGENT_PRO: 1, INVESTOR: 1, ENTERPRISE: 2 } as const;

export interface UpgradeSubscriptionResult {
  subscriptionId: string;
  previousTier: string;
  newTier: string;
  status: string;
}

@CommandHandler(UpgradeSubscriptionCommand)
export class UpgradeSubscriptionHandler implements ICommandHandler<UpgradeSubscriptionCommand> {
  constructor(
    @Inject(SUBSCRIPTION_REPOSITORY)
    private readonly subscriptionRepo: ISubscriptionRepository,
    private readonly prisma: PrismaService,
    private readonly eventBus: EventBus,
    private readonly cache: CacheService,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: UpgradeSubscriptionCommand): Promise<UpgradeSubscriptionResult> {
    try {
      const subscription = await this.subscriptionRepo.findByUserId(command.userId);
      if (!subscription) {
        throw new NotFoundException('Subscription', command.userId);
      }

      if (!subscription.isActive()) {
        throw new ValidationException('Subscription không ở trạng thái hoạt động');
      }

      // Validate upgrade direction
      const currentOrder = TIER_ORDER[subscription.planTier];
      const newOrder = TIER_ORDER[command.newPlanTier];
      if (newOrder <= currentOrder && command.newPlanTier !== subscription.planTier) {
        // Allow same-tier for AGENT_PRO <-> INVESTOR switches
        if (currentOrder !== newOrder) {
          throw new ValidationException('Chỉ có thể nâng cấp lên gói cao hơn');
        }
      }

      if (command.newPlanTier === subscription.planTier) {
        throw new ValidationException('Đã đang sử dụng gói này');
      }

      // Fetch new plan
      const newPlan = await this.prisma.plan.findFirst({
        where: { tier: command.newPlanTier, isActive: true },
      });
      if (!newPlan) {
        throw new NotFoundException('Plan', command.newPlanTier);
      }

      const previousTier = subscription.planTier;
      const upgradeResult = subscription.upgrade(newPlan.id, command.newPlanTier);
      if (upgradeResult.isErr) {
        throw upgradeResult.unwrapErr();
      }
      await this.subscriptionRepo.update(subscription);

      // Publish domain events
      const events = subscription.clearDomainEvents();
      for (const event of events) {
        this.eventBus.publish(event);
      }

      // Invalidate all cached quota entries for this user (limits change with plan)
      await this.cache.invalidateByPrefix(
        CacheService.buildKey(CachePrefix.USER_QUOTA, command.userId),
      );

      this.logger.log(
        `Subscription upgraded: id=${subscription.id}, ${previousTier} → ${command.newPlanTier}`,
        'UpgradeSubscriptionHandler',
      );

      return {
        subscriptionId: subscription.id,
        previousTier,
        newTier: command.newPlanTier,
        status: subscription.status,
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to nâng cấp subscription: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể nâng cấp gói đăng ký. Vui lòng thử lại sau.');
    }
  }
}
