import { Inject, InternalServerErrorException } from '@nestjs/common';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports -- NestJS DI requires value imports for emitDecoratorMetadata
import { CommandHandler, EventBus, ICommandHandler } from '@nestjs/cqrs';
import { createId } from '@paralleldrive/cuid2';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports -- NestJS DI requires value imports for emitDecoratorMetadata
import { ConflictException, DomainException, ValidationException, LoggerService } from '@modules/shared';
import { ReviewEntity } from '../../../domain/entities/review.entity';
import { REVIEW_REPOSITORY, type IReviewRepository } from '../../../domain/repositories/review.repository';
import { Rating } from '../../../domain/value-objects/rating.vo';
import { CreateReviewCommand } from './create-review.command';

export interface CreateReviewResult {
  id: string;
  rating: number;
  targetType: string;
  targetId: string;
  createdAt: string;
}

@CommandHandler(CreateReviewCommand)
export class CreateReviewHandler implements ICommandHandler<CreateReviewCommand> {
  constructor(
    @Inject(REVIEW_REPOSITORY) private readonly reviewRepo: IReviewRepository,
    private readonly eventBus: EventBus,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: CreateReviewCommand): Promise<CreateReviewResult> {
    try {
      // Validate rating value object
      const ratingResult = Rating.create(command.rating);
      if (ratingResult.isErr) {
        throw new ValidationException(ratingResult.unwrapErr());
      }
      const rating = ratingResult.unwrap();

      // Prevent self-review
      if (command.userId === command.targetId && command.targetType === 'user') {
        throw new ValidationException('Không thể tự đánh giá bản thân');
      }

      // One review per user per target
      const existing = await this.reviewRepo.findByUserAndTarget(
        command.userId,
        command.targetType,
        command.targetId,
      );
      if (existing) {
        throw new ConflictException('Bạn đã đánh giá mục tiêu này rồi');
      }

      const id = createId();
      const review = ReviewEntity.createNew(
        id,
        command.userId,
        command.targetType,
        command.targetId,
        rating,
        command.comment,
      );

      await this.reviewRepo.save(review);

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

      this.logger.log(`Review ${id} created by user ${command.userId} for ${command.targetType}:${command.targetId}`, 'CreateReviewHandler');

      return {
        id,
        rating: rating.value,
        targetType: command.targetType,
        targetId: command.targetId,
        createdAt: review.createdAt.toISOString(),
      };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to create review: ${error instanceof Error ? error.message : String(error)}`,
        error instanceof Error ? error.stack : undefined,
        'CreateReviewHandler',
      );
      throw new InternalServerErrorException('Lỗi khi tạo đánh giá');
    }
  }
}
