import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common';
import { CreateApprovalRuleDto } from '../dto/create-approval-rule.dto';
import { ReorderItemsDto } from '../dto/reorder-items.dto';
import { UpdateApprovalRuleDto } from '../dto/update-approval-rule.dto';
import { ApprovalRule } from '../entities/approval-rule.entity';
import {
  APPROVAL_RULES_REPOSITORY,
  IApprovalRulesRepository,
} from '../interfaces/approval-rules.repository.interface';
import { DisplayOrderService } from './display-order.service';

@Injectable()
export class ApprovalRulesService {
  constructor(
    @Inject(APPROVAL_RULES_REPOSITORY)
    private readonly repository: IApprovalRulesRepository,
    private readonly displayOrder: DisplayOrderService,
  ) {}

  /** List approval rules. */
  async findAll(filter: {
    requiresDirectorApproval?: boolean;
    page?: number;
    pageSize?: number;
  }): Promise<{ data: ApprovalRule[]; meta: { total: number; page: number; pageSize: number; totalPages: number } }> {
    const page = filter.page ?? 1;
    const pageSize = filter.pageSize ?? 10;
    const where: Record<string, unknown> = {};
    if (filter.requiresDirectorApproval !== undefined) {
      where.requiresDirectorApproval = filter.requiresDirectorApproval;
    }

    const [data, total] = await this.repository.findAndCount({
      where,
      order: { requiresDirectorApproval: 'ASC', stepOrder: 'ASC' },
      skip: (page - 1) * pageSize,
      take: pageSize,
    });
    return { data, meta: { total, page, pageSize, totalPages: Math.ceil(total / pageSize) } };
  }

  /** Get approval chain for a cargo type flag. */
  async findChain(requiresDirectorApproval: boolean): Promise<ApprovalRule[]> {
    return this.repository.findChainForCargo(requiresDirectorApproval);
  }

  /** Get an approval rule by ID. */
  async findById(id: string): Promise<ApprovalRule> {
    const entity = await this.repository.findById(id);
    if (!entity) throw new NotFoundException(`Approval rule ${id} not found`);
    return entity;
  }

  /** Create an approval rule step. */
  async create(dto: CreateApprovalRuleDto): Promise<ApprovalRule> {
    if (dto.stepOrder !== undefined && dto.insertAfterId) {
      throw new BadRequestException('Cannot set both stepOrder and insertAfterId');
    }

    const scopeWhere = { requiresDirectorApproval: dto.requiresDirectorApproval };
    const stepOrder = await this.displayOrder.resolveCreateOrder(ApprovalRule, 'stepOrder', {
      explicitOrder: dto.stepOrder,
      insertAfterId: dto.insertAfterId,
      scopeWhere,
    });

    return this.repository.create({
      requiresDirectorApproval: dto.requiresDirectorApproval,
      stepOrder,
      requiredRole: dto.requiredRole,
      actionLabel: dto.actionLabel,
      blocksRole: dto.blocksRole,
    });
  }

  /** Update an approval rule. */
  async update(id: string, dto: UpdateApprovalRuleDto): Promise<ApprovalRule> {
    await this.findById(id);
    const updated = await this.repository.update(id, dto);
    if (!updated) throw new NotFoundException(`Approval rule ${id} not found`);
    return updated;
  }

  /** Soft-delete an approval rule. */
  async remove(id: string): Promise<void> {
    await this.findById(id);
    await this.repository.softDelete(id);
  }

  async reorder(dto: ReorderItemsDto): Promise<void> {
    if (dto.requiresDirectorApproval === undefined) {
      throw new BadRequestException('requiresDirectorApproval is required for approval rule reorder');
    }
    await this.displayOrder.reorderByIds(ApprovalRule, 'stepOrder', dto.ids, {
      requiresDirectorApproval: dto.requiresDirectorApproval,
    });
  }

  async moveOrder(id: string, direction: 'up' | 'down'): Promise<void> {
    const rule = await this.findById(id);
    await this.displayOrder.moveOne(ApprovalRule, 'stepOrder', id, direction, {
      requiresDirectorApproval: rule.requiresDirectorApproval,
    });
  }
}
