export type GraphNodeLike = {
  id: string;
  domain: string;
};

export type GraphLinkLike = {
  source: string;
  target: string;
};

export type ResourceGraphSnapshot = {
  nodes: GraphNodeLike[];
  links: GraphLinkLike[];
  meta: {
    resourceEid: string;
    resourceName: string;
    [key: string]: unknown;
  };
};

export type ProjectGraphSnapshot = {
  nodes: GraphNodeLike[];
  links: GraphLinkLike[];
  meta: {
    projectCode: string;
    projectName: string;
    [key: string]: unknown;
  };
};

function filterGraphData<
  TNode extends GraphNodeLike,
  TLink extends GraphLinkLike,
>(input: {
  nodes: TNode[];
  links: TLink[];
  domain?: string;
  includeLinks?: boolean;
}) {
  const requestedDomain = input.domain?.trim().toUpperCase();
  const nodes = requestedDomain
    ? input.nodes.filter((node) => node.domain === requestedDomain)
    : input.nodes;
  const selectedNodeIds = new Set(nodes.map((node) => node.id));
  const links = input.includeLinks
    ? input.links.filter((link) => selectedNodeIds.has(link.source) && selectedNodeIds.has(link.target))
    : [];

  return {
    requestedDomain: requestedDomain ?? null,
    includedLinks: input.includeLinks ?? false,
    selectedNodeCount: nodes.length,
    selectedLinkCount: links.length,
    nodes,
    ...(input.includeLinks ? { links } : {}),
  };
}

function getAvailableDomains<TNode extends { domain: string }>(nodes: TNode[]) {
  return [...new Set(nodes.map((node) => node.domain))];
}

export function formatResourceGraphDetail(input: {
  resourceId: string;
  graph: ResourceGraphSnapshot;
  domain?: string;
  includeLinks?: boolean;
}) {
  return {
    resource: {
      id: input.resourceId,
      eid: input.graph.meta.resourceEid,
      displayName: input.graph.meta.resourceName,
    },
    availableDomains: getAvailableDomains(input.graph.nodes),
    totalNodeCount: input.graph.nodes.length,
    totalLinkCount: input.graph.links.length,
    ...filterGraphData({
      nodes: input.graph.nodes,
      links: input.graph.links,
      ...(input.domain ? { domain: input.domain } : {}),
      ...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}),
    }),
    meta: input.graph.meta,
  };
}

export function formatProjectGraphDetail(input: {
  projectId: string;
  graph: ProjectGraphSnapshot;
  domain?: string;
  includeLinks?: boolean;
}) {
  return {
    project: {
      id: input.projectId,
      shortCode: input.graph.meta.projectCode,
      name: input.graph.meta.projectName,
    },
    availableDomains: getAvailableDomains(input.graph.nodes),
    totalNodeCount: input.graph.nodes.length,
    totalLinkCount: input.graph.links.length,
    ...filterGraphData({
      nodes: input.graph.nodes,
      links: input.graph.links,
      ...(input.domain ? { domain: input.domain } : {}),
      ...(input.includeLinks !== undefined ? { includeLinks: input.includeLinks } : {}),
    }),
    meta: input.graph.meta,
  };
}
