/**
 * Git error classification and retry configuration for persistence adapters.
 *
 * Pure functions that classify raw Git plumbing errors into typed
 * PersistenceError categories. Extracted from GitGraphAdapter to keep
 * the adapter under the 500 LOC limit.
 */
import PersistenceError from "../../domain/errors/PersistenceError.js";
// ---------------------------------------------------------------------------
// Error pattern constants
// ---------------------------------------------------------------------------
/** Transient Git errors safe to retry (lock contention, I/O, timeout). */
const TRANSIENT_ERROR_PATTERNS = [
    'cannot lock ref',
    'resource temporarily unavailable',
    'connection timed out',
];
/** Stderr patterns indicating a missing Git object. */
const MISSING_OBJECT_PATTERNS = [
    'bad object',
    'not a valid object name',
    'does not point to a valid object',
    'missing object',
    'not a commit',
    'could not read',
];
/** Stderr patterns indicating a ref was not found. */
const REF_NOT_FOUND_PATTERNS = [
    'not found',
    'does not exist',
    'unknown revision',
    'bad revision',
];
/** Stderr patterns indicating a ref I/O failure. */
const REF_IO_PATTERNS = [
    'cannot lock ref',
    'unable to create',
    'permission denied',
    'failed to lock',
];
// ---------------------------------------------------------------------------
// Error inspection helpers
// ---------------------------------------------------------------------------
/** Extracts the exit code from a Git command error. */
export function getExitCode(err) {
    const raw = err.details?.code ?? err.exitCode ?? err.code;
    return typeof raw === 'number' ? raw : undefined;
}
/** Builds a lowercase search string from an error's message and stderr. */
function errorSearchText(err) {
    const msg = err.message ?? '';
    const stderr = err.details?.stderr ?? '';
    return `${msg} ${stderr}`.toLowerCase();
}
/**
 * Returns stderr+stdout diagnostic text, ignoring wrapper messages
 * like "Git command failed with code 1" that carry no object-lookup semantics.
 */
export function gitDiagnosticText(err) {
    const stderr = err.details?.stderr !== null && err.details?.stderr !== undefined ? String(err.details.stderr) : '';
    const stdout = err.details?.stdout !== null && err.details?.stdout !== undefined ? String(err.details.stdout) : '';
    return `${stderr} ${stdout}`.trim().toLowerCase();
}
/** Is the error transient and safe to retry? */
function isTransientError(error) {
    const gitErr = error;
    const message = (gitErr.message ?? '').toLowerCase();
    const stderr = (gitErr.details?.stderr ?? '').toLowerCase();
    const searchText = `${message} ${stderr}`;
    return TRANSIENT_ERROR_PATTERNS.some(pattern => searchText.includes(pattern));
}
/** Does the error indicate a dangling or missing Git object at exit code 128? */
export function isDanglingObjectError(err) {
    if (getExitCode(err) !== 128) {
        return false;
    }
    const stderr = (err.details?.stderr ?? '').toLowerCase();
    return (stderr.includes('bad object') ||
        stderr.includes('not a valid object name') ||
        stderr.includes('does not point to a valid object'));
}
/** Does the error indicate a missing Git object? */
function isMissingObjectError(err) {
    const code = getExitCode(err);
    if (code !== 128 && code !== 1) {
        return false;
    }
    const text = errorSearchText(err);
    return MISSING_OBJECT_PATTERNS.some(p => text.includes(p));
}
/** Does the error indicate a ref was not found? */
function isRefNotFoundError(err) {
    const code = getExitCode(err);
    if (code !== 128 && code !== 1) {
        return false;
    }
    const text = errorSearchText(err);
    return REF_NOT_FOUND_PATTERNS.some(p => text.includes(p));
}
/** Does the error indicate a ref I/O failure? */
function isRefIoError(err) {
    if (getExitCode(err) !== 128) {
        return false;
    }
    const text = errorSearchText(err);
    return REF_IO_PATTERNS.some(p => text.includes(p));
}
/**
 * Wraps a raw Git error in a typed PersistenceError when the failure
 * matches a known pattern. Returns the original error unchanged otherwise.
 */
export function wrapGitError(err, hint = {}) {
    if (isMissingObjectError(err)) {
        return new PersistenceError((hint.oid !== undefined && hint.oid.length > 0) ? `Missing Git object: ${hint.oid}` : err.message, PersistenceError.E_MISSING_OBJECT, { cause: err, context: { ...hint } });
    }
    if (isRefNotFoundError(err)) {
        return new PersistenceError((hint.ref !== undefined && hint.ref.length > 0) ? `Ref not found: ${hint.ref}` : err.message, PersistenceError.E_REF_NOT_FOUND, { cause: err, context: { ...hint } });
    }
    if (isRefIoError(err)) {
        return new PersistenceError((hint.ref !== undefined && hint.ref.length > 0) ? `Ref I/O error: ${hint.ref}` : err.message, PersistenceError.E_REF_IO, { cause: err, context: { ...hint } });
    }
    return err;
}
// ---------------------------------------------------------------------------
// Retry configuration
// ---------------------------------------------------------------------------
/** Default retry options for git operations. Exponential backoff with jitter. */
export const DEFAULT_RETRY_OPTIONS = {
    retries: 3,
    delay: 100,
    maxDelay: 2000,
    backoff: 'exponential',
    jitter: 'decorrelated',
    shouldRetry: isTransientError,
};
/**
 * Narrows a caught unknown to GitError for classification functions.
 * This is the single boundary parser for Git plumbing errors.
 *
 * Non-`Error` throwables are wrapped in a `PersistenceError` with
 * `E_MISSING_OBJECT` — the adapter-boundary representation of an
 * opaque git failure. The return type is `GitError | PersistenceError`
 * so downstream classifiers can read `details` / `exitCode` / `code`
 * defensively on the git-surface branch and `instanceof
 * PersistenceError` on the wrapped branch.
 */
export function toGitError(err) {
    if (err instanceof Error) {
        return err;
    }
    return new PersistenceError(String(err), PersistenceError.E_MISSING_OBJECT, {});
}
