import {
  ConflictException,
  Inject,
  Injectable,
  NotFoundException,
} from "@nestjs/common";

import { CreateFileUploadFieldDto } from "./dto/create-file-upload-field.dto";
import { CreateFileUploadSettingDto } from "./dto/create-file-upload-setting.dto";
import { UpdateFileUploadFieldDto } from "./dto/update-file-upload-field.dto";
import { UpdateFileUploadSettingDto } from "./dto/update-file-upload-setting.dto";
import { FileUploadField } from "./entities/file-upload-field.entity";
import { FileUploadSetting } from "./entities/file-upload-setting.entity";
import {
  FILE_UPLOAD_SETTINGS_REPOSITORY,
  IFileUploadSettingsRepository,
} from "./interfaces/file-upload-settings.repository.interface";

@Injectable()
export class FileUploadSettingsService {
  constructor(
    @Inject(FILE_UPLOAD_SETTINGS_REPOSITORY)
    private readonly repository: IFileUploadSettingsRepository,
  ) {}

  list(): Promise<FileUploadSetting[]> {
    return this.repository.findAll();
  }

  async getById(id: string): Promise<FileUploadSetting> {
    const setting = await this.repository.findById(id);
    if (!setting) throw new NotFoundException(`Setting ${id} not found`);
    return setting;
  }

  getByEntity(entity: string): Promise<FileUploadSetting[]> {
    return this.repository.findByEntity(entity);
  }

  async getByCode(code: string): Promise<FileUploadSetting> {
    const setting = await this.repository.findByCode(code);
    if (!setting) throw new NotFoundException(`Setting "${code}" not found`);
    return setting;
  }

  async create(dto: CreateFileUploadSettingDto): Promise<FileUploadSetting> {
    const existing = await this.repository.findByCode(dto.code);
    if (existing) {
      throw new ConflictException(
        `File upload setting with code "${dto.code}" already exists`,
      );
    }

    const setting = await this.repository.create({
      code: dto.code,
      label: dto.label,
      description: dto.description ?? null,
      entity: dto.entity,
    });

    if (dto.fields && dto.fields.length > 0) {
      await this.repository.replaceFields(setting.id, dto.fields);
    }

    return this.getById(setting.id);
  }

  async update(
    id: string,
    dto: UpdateFileUploadSettingDto,
  ): Promise<FileUploadSetting> {
    await this.getById(id); // existence check
    const updated = await this.repository.update(id, dto);
    if (!updated) throw new NotFoundException(`Setting ${id} not found`);
    return updated;
  }

  async remove(id: string): Promise<void> {
    await this.getById(id);
    await this.repository.softDelete(id);
  }

  /* ------------------------ field operations ------------------------ */

  async replaceFields(
    settingId: string,
    fields: CreateFileUploadFieldDto[],
  ): Promise<FileUploadField[]> {
    await this.getById(settingId);
    return this.repository.replaceFields(settingId, fields);
  }

  async addField(
    settingId: string,
    dto: CreateFileUploadFieldDto,
  ): Promise<FileUploadField> {
    await this.getById(settingId);
    return this.repository.addField(settingId, dto);
  }

  async updateField(
    fieldId: string,
    dto: UpdateFileUploadFieldDto,
  ): Promise<FileUploadField> {
    const updated = await this.repository.updateField(fieldId, dto);
    if (!updated) throw new NotFoundException(`Field ${fieldId} not found`);
    return updated;
  }

  async removeField(fieldId: string): Promise<void> {
    await this.repository.removeField(fieldId);
  }
}
