import { constants } from "node:fs";
import { link, lstat, mkdir, open, realpath, unlink, type FileHandle } from "node:fs/promises";
import { isAbsolute, join, relative, resolve, sep } from "node:path";
import type { Readable } from "node:stream";

export class WorkspaceFileBoundaryError extends Error {
  constructor(
    message: string,
    readonly requestedPath: string,
    readonly reason: "not_found" | "boundary" | "io" | "limit" = "boundary",
    options?: ErrorOptions,
  ) {
    super(message, options);
    this.name = "WorkspaceFileBoundaryError";
  }
}

export interface WorkspaceFileSnapshot {
  readonly path: string;
  readonly name: string;
  readonly data: Buffer;
}

export interface WorkspaceFileWriteResult {
  readonly path: string;
  readonly device: number;
  readonly inode: number;
}

interface OpenDirectory {
  readonly handle: FileHandle;
  readonly displayPath: string;
}

/** Read a stable snapshot without following any symlink below the workspace. */
export async function readWorkspaceFileNoFollow(
  workspaceRoot: string,
  workspaceDir: string,
  requestedPath: string,
  maxBytes?: number,
): Promise<WorkspaceFileSnapshot> {
  const workspace = await canonicalWorkspace(workspaceRoot, workspaceDir);
  const components = fileComponents(workspace, requestedPath);
  const name = components.at(-1)!;
  const directories = components.slice(0, -1);
  const parent = await openDirectoryTree(workspace, directories, false, requestedPath);
  const openedPath = childPath(parent, name);
  let file: FileHandle | undefined;
  let result: WorkspaceFileSnapshot | undefined;
  let failure: unknown;
  try {
    file = await open(openedPath, constants.O_RDONLY | constants.O_NOFOLLOW);
    const metadata = await file.stat();
    if (!metadata.isFile()) {
      throw new WorkspaceFileBoundaryError(`deliverable is not a regular file: ${requestedPath}`, requestedPath);
    }
    if (maxBytes !== undefined && metadata.size > maxBytes) {
      throw new WorkspaceFileBoundaryError(
        `workspace file exceeds ${maxBytes} bytes: ${requestedPath}`,
        requestedPath,
        "limit",
      );
    }
    await assertNameStillReferences(parent, name, metadata.dev, metadata.ino, requestedPath);
    const data = await readFileSnapshot(file, maxBytes, requestedPath);
    result = { path: join(workspace, ...components), name, data };
  } catch (error) {
    failure = boundaryError(error, requestedPath, "cannot read workspace file without following symlinks");
  }
  await finishWithCleanup(
    failure,
    [file, parent.handle],
    `workspace file read cleanup failed: ${requestedPath}`,
  );
  return result!;
}

async function readFileSnapshot(
  file: FileHandle,
  maxBytes: number | undefined,
  requestedPath: string,
): Promise<Buffer> {
  if (maxBytes === undefined) return file.readFile();
  const chunks: Buffer[] = [];
  let total = 0;
  let position = 0;
  while (true) {
    const remainingWithSentinel = maxBytes - total + 1;
    const chunk = Buffer.allocUnsafe(Math.min(64 * 1024, remainingWithSentinel));
    const { bytesRead } = await file.read(chunk, 0, chunk.length, position);
    if (bytesRead === 0) return Buffer.concat(chunks, total);
    total += bytesRead;
    if (total > maxBytes) {
      throw new WorkspaceFileBoundaryError(
        `workspace file exceeds ${maxBytes} bytes: ${requestedPath}`,
        requestedPath,
        "limit",
      );
    }
    chunks.push(chunk.subarray(0, bytesRead));
    position += bytesRead;
  }
}

/**
 * Create one inbound file exclusively below the workspace and stream into its
 * already-open descriptor. Linux uses /proc/self/fd-relative traversal so a
 * concurrent directory rename/symlink swap cannot redirect the write.
 */
export async function writeNewWorkspaceFileNoFollow(
  workspaceRoot: string,
  workspaceDir: string,
  requestedPath: string,
  source: Readable,
  maxBytes?: number,
): Promise<string> {
  return (await writeNewWorkspaceFileNoFollowTracked(
    workspaceRoot,
    workspaceDir,
    requestedPath,
    source,
    maxBytes,
  )).path;
}

/** Create one inbound file and return its identity for race-safe compensation. */
export async function writeNewWorkspaceFileNoFollowTracked(
  workspaceRoot: string,
  workspaceDir: string,
  requestedPath: string,
  source: Readable,
  maxBytes?: number,
): Promise<WorkspaceFileWriteResult> {
  const workspace = await canonicalWorkspace(workspaceRoot, workspaceDir);
  const components = fileComponents(workspace, requestedPath);
  const name = components.at(-1)!;
  const directories = components.slice(0, -1);
  const parent = await openDirectoryTree(workspace, directories, true, requestedPath);
  const openedPath = childPath(parent, name);
  let file: FileHandle | undefined;
  let device: number | undefined;
  let inode: number | undefined;
  let result: WorkspaceFileWriteResult | undefined;
  let failure: unknown;
  const cleanupFailures: unknown[] = [];
  try {
    file = await open(
      openedPath,
      constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW,
      0o600,
    );
    const metadata = await file.stat();
    device = metadata.dev;
    inode = metadata.ino;
    let bytesWritten = 0;
    for await (const chunk of source) {
      const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk as Uint8Array);
      bytesWritten += buffer.length;
      if (maxBytes !== undefined && bytesWritten > maxBytes) {
        throw new WorkspaceFileBoundaryError(
          `inbound file exceeds ${maxBytes} bytes: ${requestedPath}`,
          requestedPath,
          "limit",
        );
      }
      let offset = 0;
      while (offset < buffer.length) {
        const { bytesWritten } = await file.write(buffer, offset, buffer.length - offset, null);
        if (bytesWritten === 0) throw new Error("inbound workspace write made no progress");
        offset += bytesWritten;
      }
    }
    await file.sync();
    await assertNameStillReferences(parent, name, metadata.dev, metadata.ino, requestedPath);
    result = {
      path: join(workspace, ...components),
      device: metadata.dev,
      inode: metadata.ino,
    };
  } catch (error) {
    failure = boundaryError(error, requestedPath, "cannot create inbound workspace file safely");
    if (device !== undefined && inode !== undefined) {
      try {
        await unlinkIfSameFile(parent, name, device, inode);
      } catch (cleanupError) {
        cleanupFailures.push(cleanupError);
      }
    }
  }
  await finishWithCleanup(
    failure,
    [file, parent.handle],
    `inbound workspace file cleanup failed: ${requestedPath}`,
    cleanupFailures,
  );
  return result!;
}

/** Remove only the exact file created by a tracked no-follow write. */
export async function removeWorkspaceFileIfUnchangedNoFollow(
  workspaceRoot: string,
  workspaceDir: string,
  requestedPath: string,
  expectedDevice: number,
  expectedInode: number,
): Promise<void> {
  const workspace = await canonicalWorkspace(workspaceRoot, workspaceDir);
  const components = fileComponents(workspace, requestedPath);
  const name = components.at(-1)!;
  const parent = await openDirectoryTree(workspace, components.slice(0, -1), false, requestedPath);
  let failure: unknown;
  try {
    await unlinkIfSameFile(parent, name, expectedDevice, expectedInode);
  } catch (error) {
    failure = boundaryError(error, requestedPath, "cannot compensate inbound workspace file safely");
  }
  await finishWithCleanup(
    failure,
    [parent.handle],
    `inbound workspace compensation cleanup failed: ${requestedPath}`,
  );
}

/**
 * Publish a protected same-filesystem staging file with one exclusive hard-link
 * operation. The target path is traversed without following workspace symlinks.
 */
export async function linkWorkspaceFileNoFollow(
  workspaceRoot: string,
  workspaceDir: string,
  requestedPath: string,
  stagedPath: string,
): Promise<WorkspaceFileWriteResult> {
  const workspace = await canonicalWorkspace(workspaceRoot, workspaceDir);
  const components = fileComponents(workspace, requestedPath);
  const name = components.at(-1)!;
  const parent = await openDirectoryTree(workspace, components.slice(0, -1), true, requestedPath);
  let source: FileHandle | undefined;
  let result: WorkspaceFileWriteResult | undefined;
  let failure: unknown;
  const cleanupFailures: unknown[] = [];
  try {
    source = await open(stagedPath, constants.O_RDONLY | constants.O_NOFOLLOW);
    const metadata = await source.stat();
    if (!metadata.isFile()) {
      throw new WorkspaceFileBoundaryError(`staged resource is not a regular file: ${stagedPath}`, stagedPath);
    }
    const targetPath = childPath(parent, name);
    await link(stagedPath, targetPath);
    try {
      await assertNameStillReferences(parent, name, metadata.dev, metadata.ino, requestedPath);
    } catch (error) {
      await unlinkIfSameFile(parent, name, metadata.dev, metadata.ino).catch((cleanupError) => {
        cleanupFailures.push(cleanupError);
      });
      throw error;
    }
    result = {
      path: join(workspace, ...components),
      device: metadata.dev,
      inode: metadata.ino,
    };
  } catch (error) {
    failure = boundaryError(error, requestedPath, "cannot publish staged workspace file safely");
  }
  try {
    await finishWithCleanup(
      failure,
      [source, parent.handle],
      `staged workspace publication cleanup failed: ${requestedPath}`,
      cleanupFailures,
    );
  } catch (error) {
    if (result !== undefined) {
      try {
        await removeWorkspaceFileIfUnchangedNoFollow(
          workspaceRoot,
          workspaceDir,
          requestedPath,
          result.device,
          result.inode,
        );
      } catch (compensationError) {
        throw new AggregateError(
          [error, compensationError],
          `staged workspace publication and compensation both failed: ${requestedPath}`,
          { cause: error },
        );
      }
    }
    throw error;
  }
  return result!;
}

async function canonicalWorkspace(workspaceRoot: string, workspaceDir: string): Promise<string> {
  if (process.platform !== "linux") {
    throw new WorkspaceFileBoundaryError(
      "race-safe descriptor-relative workspace access requires Linux",
      workspaceDir,
    );
  }
  if (!isAbsolute(workspaceRoot) || !isAbsolute(workspaceDir)) {
    throw new WorkspaceFileBoundaryError("workspace root and project path must be absolute", workspaceDir);
  }
  const configuredRoot = resolve(workspaceRoot);
  const configuredWorkspace = resolve(workspaceDir);
  const rel = relative(configuredRoot, configuredWorkspace);
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
    throw new WorkspaceFileBoundaryError("project workspace escapes configured workspace root", workspaceDir);
  }
  const rootMetadata = await lstat(configuredRoot);
  if (rootMetadata.isSymbolicLink() || !rootMetadata.isDirectory()) {
    throw new WorkspaceFileBoundaryError("configured workspace root is not a real directory", workspaceRoot);
  }
  const root = await realpath(configuredRoot);
  const components = rel.split(sep);
  const openedWorkspace = await openDirectoryTree(root, components, false, workspaceDir);
  let result: string | undefined;
  let failure: unknown;
  try {
    const openedTarget = await realpath(`/proc/self/fd/${openedWorkspace.handle.fd}`);
    const expected = join(root, ...components);
    if (openedTarget !== expected) {
      throw new WorkspaceFileBoundaryError("project workspace contains a symlink", workspaceDir);
    }
    result = expected;
  } catch (error) {
    failure = boundaryError(error, workspaceDir, "project workspace validation failed");
  }
  await finishWithCleanup(
    failure,
    [openedWorkspace.handle],
    `project workspace validation cleanup failed: ${workspaceDir}`,
  );
  return result!;
}

function fileComponents(root: string, requestedPath: string): string[] {
  const token = requestedPath.trim();
  if (token === "" || token.includes("\0")) {
    throw new WorkspaceFileBoundaryError("workspace file path is empty or invalid", requestedPath);
  }
  const absolute = isAbsolute(token) ? resolve(token) : resolve(root, token);
  const rel = relative(root, absolute);
  if (rel === "" || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
    throw new WorkspaceFileBoundaryError(`workspace file path escapes current project: ${requestedPath}`, requestedPath);
  }
  const components = rel.split(/[\\/]/);
  if (components.some((component) => component === "" || component === "." || component === "..")) {
    throw new WorkspaceFileBoundaryError(`workspace file path is not normalized: ${requestedPath}`, requestedPath);
  }
  return components;
}

async function openDirectoryTree(
  root: string,
  components: readonly string[],
  create: boolean,
  requestedPath: string,
): Promise<OpenDirectory> {
  let current: OpenDirectory = {
    handle: await open(root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW),
    displayPath: root,
  };
  try {
    for (const component of components) {
      const path = childPath(current, component);
      let next: FileHandle;
      try {
        next = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
      } catch (error) {
        if (!create || !isErrno(error, "ENOENT")) throw error;
        try {
          await mkdir(path, { mode: 0o700 });
        } catch (mkdirError) {
          if (!isErrno(mkdirError, "EEXIST")) throw mkdirError;
        }
        next = await open(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
      }
      const previous = current;
      current = { handle: next, displayPath: join(previous.displayPath, component) };
      await previous.handle.close();
    }
    return current;
  } catch (error) {
    try {
      await current.handle.close();
    } catch (closeError) {
      throw new AggregateError(
        [error, closeError],
        `workspace path validation and directory close both failed: ${requestedPath}`,
        { cause: error },
      );
    }
    throw boundaryError(error, requestedPath, "workspace path contains a symlink or non-directory component");
  }
}

function childPath(parent: OpenDirectory, name: string): string {
  return `/proc/self/fd/${parent.handle.fd}/${name}`;
}

async function assertNameStillReferences(
  parent: OpenDirectory,
  name: string,
  device: number,
  inode: number,
  requestedPath: string,
): Promise<void> {
  const metadata = await lstat(childPath(parent, name));
  if (metadata.isSymbolicLink() || metadata.dev !== device || metadata.ino !== inode) {
    throw new WorkspaceFileBoundaryError(
      `workspace file changed during no-follow access: ${requestedPath}`,
      requestedPath,
    );
  }
}

async function unlinkIfSameFile(parent: OpenDirectory, name: string, device: number, inode: number): Promise<void> {
  const path = childPath(parent, name);
  try {
    const metadata = await lstat(path);
    if (!metadata.isSymbolicLink() && metadata.dev === device && metadata.ino === inode) {
      await unlink(path);
    }
  } catch (error) {
    if (!isErrno(error, "ENOENT")) throw error;
  }
}

function boundaryError(error: unknown, requestedPath: string, prefix: string): WorkspaceFileBoundaryError {
  if (error instanceof WorkspaceFileBoundaryError) return error;
  const detail = error instanceof Error ? error.message : String(error);
  const reason = isErrno(error, "ENOENT")
    ? "not_found"
    : isErrno(error, "ELOOP") || isErrno(error, "ENOTDIR")
      ? "boundary"
      : "io";
  return new WorkspaceFileBoundaryError(
    `${prefix}: ${requestedPath} (${detail})`,
    requestedPath,
    reason,
    { cause: error },
  );
}

async function finishWithCleanup(
  primaryFailure: unknown,
  handles: ReadonlyArray<FileHandle | undefined>,
  message: string,
  additionalFailures: readonly unknown[] = [],
): Promise<void> {
  const settled = await Promise.allSettled(
    handles.filter((handle): handle is FileHandle => handle !== undefined).map((handle) => handle.close()),
  );
  const cleanupFailures = [
    ...additionalFailures,
    ...settled.flatMap((result) => result.status === "rejected" ? [result.reason] : []),
  ];
  if (primaryFailure !== undefined && cleanupFailures.length === 0) throw primaryFailure;
  if (primaryFailure !== undefined || cleanupFailures.length > 0) {
    const failures = primaryFailure === undefined ? cleanupFailures : [primaryFailure, ...cleanupFailures];
    throw new AggregateError(failures, message, { cause: primaryFailure ?? cleanupFailures[0] });
  }
}

function isErrno(error: unknown, code: string): error is NodeJS.ErrnoException {
  return error instanceof Error && "code" in error && error.code === code;
}
