import type { CircuitSectionTransactionStore } from "../ports/circuit-section-transaction.store.js"; import { CircuitRepository } from "../../db/repositories/circuit.repository.js"; import { CircuitSectionRepository } from "../../db/repositories/circuit-section.repository.js"; import type { ReorderSectionCircuitsInput, UpdateSectionEquipmentIdentifiersInput, } from "../../shared/validation/circuit.schemas.js"; import { CircuitNumberingService } from "./circuit-numbering.service.js"; export class CircuitWriteService { private readonly circuitRepository: CircuitRepository; private readonly circuitSectionRepository: CircuitSectionRepository; private readonly circuitSectionTransactionStore?: CircuitSectionTransactionStore; private readonly numberingService: CircuitNumberingService; constructor(deps?: { circuitRepository?: CircuitRepository; circuitSectionRepository?: CircuitSectionRepository; circuitSectionTransactionStore?: CircuitSectionTransactionStore; numberingService?: CircuitNumberingService; }) { this.circuitRepository = deps?.circuitRepository ?? new CircuitRepository(); this.circuitSectionRepository = deps?.circuitSectionRepository ?? new CircuitSectionRepository(); this.circuitSectionTransactionStore = deps?.circuitSectionTransactionStore; this.numberingService = deps?.numberingService ?? new CircuitNumberingService(); } private getCircuitSectionTransactionStore() { if (!this.circuitSectionTransactionStore) { throw new Error("Circuit-section transactions are not configured."); } return this.circuitSectionTransactionStore; } async getNextIdentifier(sectionId: string) { return this.numberingService.getNextIdentifier(sectionId); } async renumberSection(sectionId: string) { // Explicit renumber operation for one section only. // Never renumbers other sections and never runs implicitly during move/sort operations. const section = await this.circuitSectionRepository.findById(sectionId); if (!section) { throw new Error("Invalid section id."); } const sectionCircuits = await this.circuitRepository.listBySection(sectionId); const otherCircuits = (await this.circuitRepository.listByCircuitList(section.circuitListId)).filter( (circuit) => circuit.sectionId !== sectionId ); const otherIdentifiers = new Set(otherCircuits.map((circuit) => circuit.equipmentIdentifier)); const finalAssignments: Array<{ id: string; equipmentIdentifier: string }> = []; let index = 1; for (const circuit of sectionCircuits) { let candidate = `${section.prefix}${index}`; while (otherIdentifiers.has(candidate)) { index += 1; candidate = `${section.prefix}${index}`; } finalAssignments.push({ id: circuit.id, equipmentIdentifier: candidate }); index += 1; } // Uses safe two-phase identifier update to avoid UNIQUE collisions during swaps. this.getCircuitSectionTransactionStore().updateEquipmentIdentifiers( section.circuitListId, finalAssignments, sectionId ); return this.circuitRepository.listBySection(sectionId); } async updateSectionEquipmentIdentifiers( sectionId: string, input: UpdateSectionEquipmentIdentifiersInput ) { const section = await this.circuitSectionRepository.findById(sectionId); if (!section) { throw new Error("Invalid section id."); } const sectionCircuits = await this.circuitRepository.listBySection(sectionId); const sectionIds = new Set(sectionCircuits.map((circuit) => circuit.id)); if (input.identifiers.length !== sectionCircuits.length) { throw new Error("identifiers must include all circuits in the section."); } for (const entry of input.identifiers) { if (!sectionIds.has(entry.circuitId)) { throw new Error("Circuit id does not belong to section."); } } this.getCircuitSectionTransactionStore().updateEquipmentIdentifiers( section.circuitListId, input.identifiers.map((entry) => ({ id: entry.circuitId, equipmentIdentifier: entry.equipmentIdentifier })), sectionId ); return this.circuitRepository.listBySection(sectionId); } async reorderCircuitsInSection(sectionId: string, input: ReorderSectionCircuitsInput) { // Reorder updates sortOrder only. BMKs remain unchanged; users may renumber explicitly later. const section = await this.circuitSectionRepository.findById(sectionId); if (!section) { throw new Error("Invalid section id."); } const sectionCircuits = await this.circuitRepository.listBySection(sectionId); const sectionIds = new Set(sectionCircuits.map((circuit) => circuit.id)); if (sectionCircuits.length !== input.orderedCircuitIds.length) { throw new Error("orderedCircuitIds must include all circuits of the section."); } for (const circuitId of input.orderedCircuitIds) { if (!sectionIds.has(circuitId)) { throw new Error("Circuit id does not belong to section."); } } this.getCircuitSectionTransactionStore().updateSortOrders( sectionId, input.orderedCircuitIds ); return this.circuitRepository.listBySection(sectionId); } }