import type { Employee, NormalizedOrgChartDataset, OrganizationTreeNode, OrgChartDataset } from './types.js'; /** Pure derivation for validated datasets. Multiple roots remain a forest. */ export function normalizeOrgChartDataset(data: OrgChartDataset): NormalizedOrgChartDataset { const organizationsById = new Map(data.organizations.map(org => [org.id, org])); const employeesByOrganization = new Map(data.organizations.map(org => [org.id, []])); for (const employee of data.employees) employeesByOrganization.get(employee.organizationId)?.push(employee); const nodes = new Map(data.organizations.map(org => [org.id, { organization: org, children: [] }])); const roots: OrganizationTreeNode[] = []; for (const node of nodes.values()) { if (node.organization.parentId === null) roots.push(node); else nodes.get(node.organization.parentId)?.children.push(node); } const order = new Map((data.presentation?.organizationOrder ?? []).map((id, index) => [id, index])); const compare = (a: OrganizationTreeNode, b: OrganizationTreeNode) => { const aOrder = order.get(a.organization.id) ?? Infinity; const bOrder = order.get(b.organization.id) ?? Infinity; if (aOrder !== bOrder) return aOrder < bOrder ? -1 : 1; const difference = a.organization.displayOrder - b.organization.displayOrder; if (difference) return difference; // Code-unit order is deterministic across environments and independent of locale. return a.organization.id < b.organization.id ? -1 : a.organization.id > b.organization.id ? 1 : 0; }; roots.sort(compare); for (const node of nodes.values()) node.children.sort(compare); return { roots, organizationsById, employeesByOrganization }; }