import { BaseRepository } from "@edr/api-common";
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { In, Repository } from "typeorm";

import { FileRecord } from "./entities/file.entity";

@Injectable()
export class FilesRepository extends BaseRepository<FileRecord> {
  constructor(
    @InjectRepository(FileRecord)
    repository: Repository<FileRecord>,
  ) {
    super(repository);
  }

  findByResource(resourceId: string, resource: string): Promise<FileRecord[]> {
    return this.repository.find({ where: { resourceId, resource } });
  }

  /**
   * Batch sibling of {@link findByResource} for hydrating a page of resources at
   * once (a thread of chat messages, say) instead of one query per row.
   */
  async findByResourceIds(
    resourceIds: string[],
    resource: string,
  ): Promise<FileRecord[]> {
    if (resourceIds.length === 0) return [];
    return this.repository.find({
      where: { resourceId: In(resourceIds), resource },
      order: { createdAt: "ASC" },
    });
  }

  findByCode(
    resourceId: string,
    resource: string,
    code: string,
  ): Promise<FileRecord | null> {
    return this.repository.findOne({ where: { resourceId, resource, code } });
  }

  /**
   * Retire the live version(s) of a document code. SOFT delete on purpose: the
   * bytes and the row stay so the original upload can still be read back from
   * the version history after staff replace it. Every normal read already
   * filters soft-deleted rows, so callers see only the current version.
   *
   * `replacedBy` / `reason` are stamped on the retired row when a newer file is
   * taking its place (as opposed to a plain removal).
   */
  async deleteByCode(
    resourceId: string,
    resource: string,
    code: string,
    replacedBy?: { userId?: string | null; reason?: string | null },
  ): Promise<void> {
    if (replacedBy) {
      await this.repository.update(
        { resourceId, resource, code },
        {
          replacedByUserId: replacedBy.userId ?? null,
          replaceReason: replacedBy.reason ?? null,
        },
      );
    }
    await this.repository.softDelete({ resourceId, resource, code });
  }

  /**
   * Every version of one document code, newest first — superseded versions
   * included. The only read that deliberately looks past the soft-delete filter.
   */
  findVersionHistory(
    resourceId: string,
    resource: string,
    code: string,
  ): Promise<FileRecord[]> {
    return this.repository.find({
      where: { resourceId, resource, code },
      withDeleted: true,
      order: { createdAt: "DESC" },
    });
  }

  /**
   * Documents belonging to any of the given resources that a reviewer has asked
   * the customer to correct. Used by the approval gate, so it takes a list of
   * resource ids (a company plus each of its company profiles) in one query.
   */
  async findWithOpenChangeRequest(
    resourceIds: string[],
    resource: string,
  ): Promise<FileRecord[]> {
    if (resourceIds.length === 0) return [];
    return this.repository.find({
      where: {
        resourceId: In(resourceIds),
        resource,
        reviewStatus: "change_requested",
      },
      order: { createdAt: "ASC" },
    });
  }
}
