import { Injectable } from '@nestjs/common';
import { JOBS_ASSET_PAGINATION_SIZE } from 'src/constants';
import { OnEvent, OnJob } from 'src/decorators';
import { BulkIdsDto } from 'src/dtos/asset-ids.response.dto';
import { AuthDto } from 'src/dtos/auth.dto';
import { TrashResponseDto } from 'src/dtos/trash.dto';
import { JobName, JobStatus, Permission, QueueName } from 'src/enum';
import { BaseService } from 'src/services/base.service';

@Injectable()
export class TrashService extends BaseService {
  async restoreAssets(auth: AuthDto, dto: BulkIdsDto): Promise<TrashResponseDto> {
    const { ids } = dto;
    if (ids.length === 0) {
      return { count: 0 };
    }

    await this.requireAccess({ auth, permission: Permission.AssetDelete, ids });
    await this.trashRepository.restoreAll(ids);
    await this.eventRepository.emit('AssetRestoreAll', { assetIds: ids, userId: auth.user.id });

    this.logger.log(`Restored ${ids.length} asset(s) from trash`);

    return { count: ids.length };
  }

  async restore(auth: AuthDto): Promise<TrashResponseDto> {
    const count = await this.trashRepository.restore(auth.user.id);
    if (count > 0) {
      this.logger.log(`Restored ${count} asset(s) from trash`);
    }
    return { count };
  }

  async empty(auth: AuthDto): Promise<TrashResponseDto> {
    const count = await this.trashRepository.empty(auth.user.id);
    if (count > 0) {
      await this.jobRepository.queue({ name: JobName.AssetEmptyTrash, data: {} });
    }
    return { count };
  }

  @OnEvent({ name: 'AssetDeleteAll' })
  async onAssetsDelete() {
    await this.jobRepository.queue({ name: JobName.AssetEmptyTrash, data: {} });
  }

  @OnJob({ name: JobName.AssetEmptyTrash, queue: QueueName.BackgroundTask })
  async handleEmptyTrash() {
    const assets = this.trashRepository.getDeletedIds();

    let count = 0;
    const batch: string[] = [];
    for await (const { id } of assets) {
      batch.push(id);

      if (batch.length === JOBS_ASSET_PAGINATION_SIZE) {
        await this.handleBatch(batch);
        count += batch.length;
        batch.length = 0;
      }
    }

    await this.handleBatch(batch);
    count += batch.length;
    batch.length = 0;

    this.logger.log(`Queued ${count} asset(s) for deletion from the trash`);

    return JobStatus.Success;
  }

  private async handleBatch(ids: string[]) {
    this.logger.debug(`Queueing ${ids.length} asset(s) for deletion from the trash`);
    await this.jobRepository.queueAll(
      ids.map((assetId) => ({
        name: JobName.AssetDelete,
        data: {
          id: assetId,
          deleteOnDisk: true,
        },
      })),
    );
  }
}
