import { Inject, InternalServerErrorException } from '@nestjs/common';
import { CommandHandler, type ICommandHandler } from '@nestjs/cqrs';
import { createId } from '@paralleldrive/cuid2';
import { DomainException, LoggerService, NotFoundException, ValidationException } from '@modules/shared';
import { PropertyMediaEntity } from '../../../domain/entities/property-media.entity';
import { PROPERTY_REPOSITORY, type IPropertyRepository } from '../../../domain/repositories/property.repository';
import { MEDIA_STORAGE_SERVICE, type IMediaStorageService } from '../../../infrastructure/services/media-storage.service';
import { UploadMediaCommand } from './upload-media.command';

const MAX_MEDIA_PER_PROPERTY = 20;

@CommandHandler(UploadMediaCommand)
export class UploadMediaHandler implements ICommandHandler<UploadMediaCommand> {
  constructor(
    @Inject(PROPERTY_REPOSITORY) private readonly propertyRepo: IPropertyRepository,
    @Inject(MEDIA_STORAGE_SERVICE) private readonly mediaStorage: IMediaStorageService,
    private readonly logger: LoggerService,
  ) {}

  async execute(command: UploadMediaCommand): Promise<{ mediaId: string; url: string }> {
    try {
      const property = await this.propertyRepo.findById(command.propertyId);
      if (!property) {
        throw new NotFoundException('Property', command.propertyId);
      }

      const mediaCount = await this.propertyRepo.countMediaByPropertyId(command.propertyId);
      if (mediaCount >= MAX_MEDIA_PER_PROPERTY) {
        throw new ValidationException(`Tối đa ${MAX_MEDIA_PER_PROPERTY} ảnh/video cho mỗi bất động sản`);
      }

      const mediaType = command.file.mimetype.startsWith('video/') ? 'video' as const : 'image' as const;

      let url: string;
      try {
        url = await this.mediaStorage.upload(
          command.file.buffer,
          command.file.originalname,
          command.file.mimetype,
          `properties/${command.propertyId}`,
        );
      } catch (error) {
        this.logger.error(
          `Media upload failed for property ${command.propertyId}: ${error instanceof Error ? error.message : error}`,
          error instanceof Error ? error.stack : undefined,
          'UploadMediaHandler',
        );
        throw new ValidationException('Tải lên media thất bại, vui lòng thử lại');
      }

      const mediaId = createId();
      const media = PropertyMediaEntity.createNew(
        mediaId,
        command.propertyId,
        url,
        mediaType,
        mediaCount, // next order index
        command.caption,
      );

      await this.propertyRepo.addMedia(media);

      return { mediaId, url };
    } catch (error) {
      if (error instanceof DomainException) throw error;
      this.logger.error(
        `Failed to upload media for property ${command.propertyId}: ${error instanceof Error ? error.message : error}`,
        error instanceof Error ? error.stack : undefined,
        this.constructor.name,
      );
      throw new InternalServerErrorException('Không thể tải lên hình ảnh/video cho bất động sản');
    }
  }
}
