import fs from "node:fs";
import { resolveUserPath } from "../utils.js";
import { openVerifiedFileSync } from "./safe-open-sync.js";

export const DEFAULT_SECRET_FILE_MAX_BYTES = 16 * 1024;

export type SecretFileReadOptions = {
  maxBytes?: number;
  rejectSymlink?: boolean;
};

export type SecretFileReadResult =
  | {
      ok: true;
      secret: string;
      resolvedPath: string;
    }
  | {
      ok: false;
      message: string;
      resolvedPath?: string;
      error?: unknown;
    };

function normalizeSecretReadError(error: unknown): Error {
  return error instanceof Error ? error : new Error(String(error));
}

export function loadSecretFileSync(
  filePath: string,
  label: string,
  options: SecretFileReadOptions = {},
): SecretFileReadResult {
  const trimmedPath = filePath.trim();
  const resolvedPath = resolveUserPath(trimmedPath);
  if (!resolvedPath) {
    return { ok: false, message: `${label} file path is empty.` };
  }

  const maxBytes = options.maxBytes ?? DEFAULT_SECRET_FILE_MAX_BYTES;

  let previewStat: fs.Stats;
  try {
    previewStat = fs.lstatSync(resolvedPath);
  } catch (error) {
    const normalized = normalizeSecretReadError(error);
    return {
      ok: false,
      resolvedPath,
      error: normalized,
      message: `Failed to inspect ${label} file at ${resolvedPath}: ${String(normalized)}`,
    };
  }

  if (options.rejectSymlink && previewStat.isSymbolicLink()) {
    return {
      ok: false,
      resolvedPath,
      message: `${label} file at ${resolvedPath} must not be a symlink.`,
    };
  }
  if (!previewStat.isFile()) {
    return {
      ok: false,
      resolvedPath,
      message: `${label} file at ${resolvedPath} must be a regular file.`,
    };
  }
  if (previewStat.size > maxBytes) {
    return {
      ok: false,
      resolvedPath,
      message: `${label} file at ${resolvedPath} exceeds ${maxBytes} bytes.`,
    };
  }

  const opened = openVerifiedFileSync({
    filePath: resolvedPath,
    rejectPathSymlink: options.rejectSymlink,
    maxBytes,
  });
  if (!opened.ok) {
    const error = normalizeSecretReadError(
      opened.reason === "validation" ? new Error("security validation failed") : opened.error,
    );
    return {
      ok: false,
      resolvedPath,
      error,
      message: `Failed to read ${label} file at ${resolvedPath}: ${String(error)}`,
    };
  }

  try {
    const raw = fs.readFileSync(opened.fd, "utf8");
    const secret = raw.trim();
    if (!secret) {
      return {
        ok: false,
        resolvedPath,
        message: `${label} file at ${resolvedPath} is empty.`,
      };
    }
    return { ok: true, secret, resolvedPath };
  } catch (error) {
    const normalized = normalizeSecretReadError(error);
    return {
      ok: false,
      resolvedPath,
      error: normalized,
      message: `Failed to read ${label} file at ${resolvedPath}: ${String(normalized)}`,
    };
  } finally {
    fs.closeSync(opened.fd);
  }
}

export function readSecretFileSync(
  filePath: string,
  label: string,
  options: SecretFileReadOptions = {},
): string {
  const result = loadSecretFileSync(filePath, label, options);
  if (result.ok) {
    return result.secret;
  }
  throw new Error(result.message, result.error ? { cause: result.error } : undefined);
}

export function tryReadSecretFileSync(
  filePath: string | undefined,
  label: string,
  options: SecretFileReadOptions = {},
): string | undefined {
  if (!filePath?.trim()) {
    return undefined;
  }
  const result = loadSecretFileSync(filePath, label, options);
  return result.ok ? result.secret : undefined;
}
