import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateIconDto } from '../dto/create-icon.dto';
import { UpdateIconDto } from '../dto/update-icon.dto';
import { IconRepository } from '../repositories/icon.repository';
import { EntityManager } from '@mikro-orm/postgresql';
import { RequiredEntityData } from '@mikro-orm/core';
import { Icon } from '../entities/icon.entity';
import { IconMessage, GroupMessage } from 'src/common/enums/message.enum';
import { Group } from '../entities/group.entity';
import { UploaderService } from 'src/modules/uploader/providers/uploader.service';
import type { File } from '@nest-lab/fastify-multer';

@Injectable()
export class IconService {
  constructor(
    private readonly iconRepository: IconRepository,
    private readonly em: EntityManager,
    private readonly uploaderService: UploaderService,
  ) {}

  async create(dto: CreateIconDto): Promise<Icon> {
    const group = await this.findGroupOrFail(dto.groupId);

    const data: RequiredEntityData<Icon> = {
      url: dto.url,
      group: group,
    };

    const icon = this.iconRepository.create(data);
    await this.em.persistAndFlush(icon);
    return icon;
  }

  async createBatch(groupId: string, files: File[]): Promise<Icon[]> {
    const group = await this.findGroupOrFail(groupId);
    const uploadedFiles = await this.uploaderService.uploadMultiple(files);

    const icons = uploadedFiles.map(({ url }) =>
      this.iconRepository.create({
        url,
        group,
      }),
    );

    await this.em.persistAndFlush(icons);
    return icons;
  }

  async findAll(): Promise<Icon[]> {
    return this.iconRepository.find({}, { populate: ['group'] });
  }

  async findOne(id: string): Promise<Icon> {
    const icon = await this.iconRepository.findOne({ id }, { populate: ['group'] });
    if (!icon) {
      throw new NotFoundException(IconMessage.NOT_FOUND);
    }
    return icon;
  }

  async update(id: string, dto: UpdateIconDto): Promise<Icon> {
    const icon = await this.iconRepository.findOne({ id });
    if (!icon) {
      throw new NotFoundException(IconMessage.NOT_FOUND);
    }

    if (dto.groupId) {
      const group = await this.em.findOne(Group, { id: dto.groupId });
      if (!group) {
        throw new NotFoundException(GroupMessage.NOT_FOUND);
      }
      icon.group = group;
    }

    this.em.assign(icon, {
      url: dto.url,
    });

    await this.em.persistAndFlush(icon);
    return icon;
  }

  private async findGroupOrFail(groupId: string): Promise<Group> {
    const group = await this.em.findOne(Group, { id: groupId });
    if (!group) {
      throw new NotFoundException(GroupMessage.NOT_FOUND);
    }

    return group;
  }

  async remove(id: string): Promise<void> {
    const icon = await this.iconRepository.findOne({ id });
    if (!icon) {
      throw new NotFoundException(IconMessage.NOT_FOUND);
    }
    await this.em.removeAndFlush(icon);
  }
}
