import { PaginatedResponse } from '@edr/types';
import { Injectable } from '@nestjs/common';
import { DataSource, FindManyOptions, Repository } from 'typeorm';
import { paginateQuery } from '../../../common/utils/pagination.util';
import { ListApprovalRulesQueryDto } from '../dto/list-rule-engine-query.dto';
import { ApprovalRule } from '../entities/approval-rule.entity';
import { IApprovalRulesRepository } from '../interfaces/approval-rules.repository.interface';

@Injectable()
export class ApprovalRulesRepository implements IApprovalRulesRepository {
  private readonly repo: Repository<ApprovalRule>;

  constructor(private readonly dataSource: DataSource) {
    this.repo = this.dataSource.getRepository(ApprovalRule);
  }

  findById(id: string): Promise<ApprovalRule | null> {
    return this.repo.findOne({ where: { id } });
  }

  findChainForCargo(requiresDirectorApproval: boolean): Promise<ApprovalRule[]> {
    return this.repo.find({
      where: { requiresDirectorApproval },
      order: { stepOrder: 'ASC' },
    });
  }

  findAll(options?: FindManyOptions<ApprovalRule>): Promise<ApprovalRule[]> {
    return this.repo.find(options);
  }

  findAndCount(options?: FindManyOptions<ApprovalRule>): Promise<[ApprovalRule[], number]> {
    return this.repo.findAndCount(options);
  }

  /**
   * Paged list in the standard envelope. Chain grouping is preserved: rows are
   * grouped by chain (requiresDirectorApproval) first, then step order.
   */
  findPaged(query: ListApprovalRulesQueryDto): Promise<PaginatedResponse<ApprovalRule>> {
    const qb = this.repo
      .createQueryBuilder('rule')
      .orderBy('rule.requiresDirectorApproval', 'ASC')
      .addOrderBy('rule.stepOrder', query.sortOrder ?? 'ASC');

    if (query.requiresDirectorApproval !== undefined) {
      qb.andWhere('rule.requiresDirectorApproval = :requiresDirectorApproval', {
        requiresDirectorApproval: query.requiresDirectorApproval,
      });
    }
    if (query.search) {
      qb.andWhere(
        '(rule.actionLabel ILIKE :search OR rule.requiredRole ILIKE :search OR rule.blocksRole ILIKE :search)',
        { search: `%${query.search}%` },
      );
    }

    return paginateQuery(qb, query);
  }

  async create(data: Partial<ApprovalRule>): Promise<ApprovalRule> {
    const entity = this.repo.create(data);
    return this.repo.save(entity);
  }

  async update(id: string, data: Partial<ApprovalRule>): Promise<ApprovalRule | null> {
    await this.repo.update(id, data);
    return this.findById(id);
  }

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