import type { TaskId } from "@awp/contracts";
import type { Task } from "./model.js";

export class DependencyCycleError extends Error {
  constructor(readonly cycle: readonly TaskId[]) {
    super(`Task dependency cycle: ${cycle.join(" -> ")}`);
  }
}
export class InvalidDependencyError extends Error {}
export function validateTaskDependencies(tasks: readonly Task[]): void {
  const byId = new Map(tasks.map((t) => [t.id, t] as const));
  for (const task of tasks)
    for (const dep of task.dependencyIds) {
      const prerequisite = byId.get(dep);
      if (!prerequisite)
        throw new InvalidDependencyError(`Task ${task.id} depends on unknown task ${dep}`);
      if (prerequisite.projectId !== task.projectId)
        throw new InvalidDependencyError(
          `Task ${task.id} depends on task ${dep} from another Project`,
        );
    }
  const visiting = new Set<TaskId>();
  const visited = new Set<TaskId>();
  const path: TaskId[] = [];
  const visit = (id: TaskId): void => {
    if (visited.has(id)) return;
    if (visiting.has(id)) {
      const i = path.indexOf(id);
      throw new DependencyCycleError([...path.slice(i), id]);
    }
    visiting.add(id);
    path.push(id);
    for (const dep of byId.get(id)?.dependencyIds ?? []) visit(dep);
    path.pop();
    visiting.delete(id);
    visited.add(id);
  };
  for (const task of tasks) visit(task.id);
}
export function unsatisfiedDependencies(task: Task, tasks: readonly Task[]): readonly TaskId[] {
  const byId = new Map(tasks.map((t) => [t.id, t] as const));
  return task.dependencyIds.filter((id) => byId.get(id)?.status !== "completed");
}
