import { constants } from 'node:fs';
import { chmod, mkdir, open, readFile, rename, rm, stat, unlink, writeFile } from 'node:fs/promises';
import { dlopen, FFIType } from 'bun:ffi';
import { isAbsolute, join } from 'node:path';
import { configDir } from './paths';

export const HOOK_CONTROL_REGISTRY = { 'background-jobs-blocker': { defaultEnabled: false } } as const;
export type HookControlId = keyof typeof HOOK_CONTROL_REGISTRY;
export type HookControlsPersistence = 'confirmed' | 'indeterminate';
export type PersistedHookControls = { version: 'hook-controls/v1'; hooks: Record<string, boolean> };
export type EffectiveHookControls = { version: 'hook-controls/v1'; hooks: Record<HookControlId, boolean> };
export type HookControlsIssue = {
  code: 'invalid-hook-controls' | 'wrong-hook-controls-version';
  detail: string;
  repairable: boolean;
};
export type HookControlsResponse = {
  controls: EffectiveHookControls;
  issue: null | HookControlsIssue;
  persistence?: HookControlsPersistence;
  persistenceDetail?: string;
};

export function isRegisteredHookControl(id: string): id is HookControlId {
  return Object.hasOwn(HOOK_CONTROL_REGISTRY, id);
}

export class HookControlsError extends Error {
  constructor(
    readonly code: 'invalid' | 'locked' | 'write-failed' | 'unknown',
    message: string,
  ) { super(message); }
}

const FILE = 'hook-controls.json';
const LOCK_FILE = '.hook-controls.lock';
const LOCK_WAIT_MS = 2_000;
const GET_FSYNC_RETRIES = 3;
const LOCK_SH = 1;
const LOCK_EX = 2;
const LOCK_NB = 4;
const LOCK_UN = 8;
const O_NOFOLLOW = constants.O_NOFOLLOW ?? 0x10000;

type RawState = { raw: Buffer | null; parsed?: PersistedHookControls; issue: HookControlsResponse['issue'] };

let flockImpl: ((fd: number, op: number) => number) | null = null;
function advisoryFlock(fd: number, op: number): number {
  if (!flockImpl) {
    try {
      const libc = dlopen('libc.so.6', { flock: { args: [FFIType.i32, FFIType.i32], returns: FFIType.i32 } });
      flockImpl = libc.symbols.flock as (fd: number, op: number) => number;
    } catch {
      throw new HookControlsError('write-failed', 'advisory flock is unavailable on this host');
    }
  }
  return flockImpl(fd, op);
}

async function defaultSyncFile(path: string): Promise<void> {
  const handle = await open(path, 'r');
  try { await handle.sync(); } finally { await handle.close(); }
}

async function defaultSyncDirectory(dir: string): Promise<void> {
  const handle = await open(dir, 'r');
  try { await handle.sync(); } finally { await handle.close(); }
}

let syncFileImpl = defaultSyncFile;
let syncDirectoryImpl = defaultSyncDirectory;
let chmodImpl = chmod;
let rmImpl = rm;
let readRawImpl: ((dir: string) => Promise<RawState>) | null = null;
let lockInitFailure: Error | null = null;
let lockReleaseFailure: Error | null = null;
let lockDescriptorCloseCount = 0;
let backupWriteAfterCreateFailure: Error | null = null;
let backupSyncFailure: Error | null = null;

/** @internal test seam for injected fsync and lock failures */
export const hookControlsInternals = {
  setSyncFile(fn: (path: string) => Promise<void>) { syncFileImpl = fn; },
  setSyncDirectory(fn: (dir: string) => Promise<void>) { syncDirectoryImpl = fn; },
  setChmod(fn: typeof chmod) { chmodImpl = fn; },
  setRm(fn: typeof rm) { rmImpl = fn; },
  setReadRaw(fn: ((dir: string) => Promise<RawState>) | null) { readRawImpl = fn; },
  setLockInitFailure(error: Error | null) { lockInitFailure = error; },
  setLockReleaseFailure(error: Error | null) { lockReleaseFailure = error; },
  setBackupWriteAfterCreateFailure(error: Error | null) { backupWriteAfterCreateFailure = error; },
  setBackupSyncFailure(error: Error | null) { backupSyncFailure = error; },
  getLockDescriptorCloseCount() { return lockDescriptorCloseCount; },
  defaultSyncDirectory,
  defaultReadRaw,
  reset() {
    syncFileImpl = defaultSyncFile;
    syncDirectoryImpl = defaultSyncDirectory;
    chmodImpl = chmod;
    rmImpl = rm;
    readRawImpl = null;
    lockInitFailure = null;
    lockReleaseFailure = null;
    lockDescriptorCloseCount = 0;
    backupWriteAfterCreateFailure = null;
    backupSyncFailure = null;
  },
};

async function syncFile(path: string): Promise<void> { return syncFileImpl(path); }
async function syncDirectory(dir: string): Promise<void> { return syncDirectoryImpl(dir); }
async function chmodPath(path: string, mode: number): Promise<void> { await chmodImpl(path, mode); }
async function removeTemp(path: string): Promise<void> { await rmImpl(path, { force: true }); }

async function syncDirectoryWithRetries(dir: string): Promise<void> {
  let lastError: unknown;
  for (let attempt = 0; attempt < GET_FSYNC_RETRIES; attempt++) {
    try {
      await syncDirectory(dir);
      return;
    } catch (error) {
      lastError = error;
      if (attempt < GET_FSYNC_RETRIES - 1) await Bun.sleep(25);
    }
  }
  throw lastError;
}

function directory(injected?: string): string {
  const value = injected ?? configDir();
  if (!value || !isAbsolute(value)) throw new HookControlsError('write-failed', 'hook controls directory must be absolute');
  return value;
}

function defaults(): EffectiveHookControls {
  const hooks = {} as Record<HookControlId, boolean>;
  for (const id of Object.keys(HOOK_CONTROL_REGISTRY) as HookControlId[]) hooks[id] = HOOK_CONTROL_REGISTRY[id].defaultEnabled;
  return { version: 'hook-controls/v1', hooks };
}

function parse(raw: string): { persisted?: PersistedHookControls; issue?: HookControlsIssue } {
  let value: unknown;
  try { value = JSON.parse(raw); } catch { return { issue: { code: 'invalid-hook-controls', detail: 'hook-controls.json is not valid JSON', repairable: true } }; }
  if (!value || typeof value !== 'object' || Array.isArray(value)) return { issue: { code: 'invalid-hook-controls', detail: 'hook-controls.json must be an object', repairable: true } };
  const record = value as Record<string, unknown>;
  if (record.version !== 'hook-controls/v1') return { issue: { code: 'wrong-hook-controls-version', detail: 'hook-controls.json has an unsupported version', repairable: true } };
  if (!record.hooks || typeof record.hooks !== 'object' || Array.isArray(record.hooks) || Object.values(record.hooks).some((v) => typeof v !== 'boolean')) return { issue: { code: 'invalid-hook-controls', detail: 'hook-controls.json hooks must contain only boolean values', repairable: true } };
  return { persisted: { version: 'hook-controls/v1', hooks: record.hooks as Record<string, boolean> } };
}

function response(parsed?: PersistedHookControls, issue: HookControlsResponse['issue'] = null, persistence: HookControlsPersistence = 'confirmed', persistenceDetail?: string): HookControlsResponse {
  const effective = defaults();
  if (parsed) for (const id of Object.keys(HOOK_CONTROL_REGISTRY) as HookControlId[]) effective.hooks[id] = parsed.hooks[id] ?? HOOK_CONTROL_REGISTRY[id].defaultEnabled;
  return {
    controls: effective,
    issue,
    ...(persistence === 'confirmed' && persistenceDetail === undefined ? {} : { persistence, ...(persistenceDetail === undefined ? {} : { persistenceDetail }) }),
  };
}

function markIndeterminate(result: HookControlsResponse, detail: string): HookControlsResponse {
  if (result.persistence === 'indeterminate') {
    return { ...result, persistenceDetail: `${result.persistenceDetail}; ${detail}` };
  }
  return { ...result, persistence: 'indeterminate', persistenceDetail: detail };
}

function isHookControlsResponse(value: unknown): value is HookControlsResponse {
  return typeof value === 'object' && value !== null && 'controls' in value && 'issue' in value;
}

async function defaultReadRaw(dir: string): Promise<RawState> {
  const path = join(dir, FILE);
  try {
    const info = await stat(path);
    if (!info.isFile()) return { raw: null, issue: { code: 'invalid-hook-controls', detail: 'hook-controls.json is not a regular file', repairable: false } };
    const raw = await readFile(path);
    const result = parse(raw.toString('utf8'));
    return { raw, parsed: result.persisted, issue: result.issue ?? null };
  } catch (error) {
    if ((error as NodeJS.ErrnoException).code === 'ENOENT') return { raw: null, issue: null };
    return { raw: null, issue: { code: 'invalid-hook-controls', detail: 'hook-controls.json cannot be read', repairable: false } };
  }
}

async function readRaw(dir: string): Promise<RawState> {
  return readRawImpl ? readRawImpl(dir) : defaultReadRaw(dir);
}

async function committedObservedResponse(
  dir: string,
  written: ReturnType<typeof parse>,
  fsyncDetail: string,
): Promise<HookControlsResponse> {
  try {
    const state = await readRaw(dir);
    if (!state.issue && state.parsed) return response(state.parsed, state.issue, 'indeterminate', fsyncDetail);
    const rereadDetail = state.issue?.detail ?? 'hook-controls.json cannot be reread after commit';
    if (written.persisted) {
      return markIndeterminate(response(written.persisted, written.issue ?? null), `${fsyncDetail}; reread failed: ${rereadDetail}`);
    }
    return response(state.parsed, state.issue, 'indeterminate', `${fsyncDetail}; reread failed: ${rereadDetail}`);
  } catch (error) {
    if (written.persisted) {
      return markIndeterminate(response(written.persisted, written.issue ?? null), `${fsyncDetail}; reread failed: ${String(error)}`);
    }
    throw new HookControlsError('write-failed', `${fsyncDetail}; reread failed: ${String(error)}`);
  }
}

async function observedResponse(dir: string, detail: string): Promise<HookControlsResponse> {
  const state = await readRaw(dir);
  return response(state.parsed, state.issue, 'indeterminate', detail);
}

export async function readHookControls(configDirectory?: string): Promise<HookControlsResponse> {
  const dir = directory(configDirectory);
  const release = await lock(dir, 'shared');
  let result: HookControlsResponse;
  try {
    const state = await readRaw(dir);
    try {
      await syncDirectoryWithRetries(dir);
      result = response(state.parsed, state.issue);
    } catch (error) {
      const detail = `config directory fsync failed during read: ${String(error)}`;
      result = response(state.parsed, state.issue, 'indeterminate', detail);
    }
  } catch (error) {
    try {
      await release();
    } catch (releaseError) {
      throw releaseError;
    }
    throw error;
  }
  try {
    await release();
    return result;
  } catch (releaseError) {
    return markIndeterminate(result, `lock release failed after read confirm: ${String(releaseError)}`);
  }
}

function validateLockStat(info: { isFile(): boolean; uid: number; nlink: number; mode: number }): void {
  if (!info.isFile()) throw new HookControlsError('write-failed', 'hook controls lock is not a regular file');
  const uid = typeof process.getuid === 'function' ? process.getuid() : undefined;
  if (uid !== undefined && info.uid !== uid) throw new HookControlsError('write-failed', 'hook controls lock is not owned by the current user');
  if (info.nlink !== 1) throw new HookControlsError('write-failed', 'hook controls lock must not be hard-linked');
  if ((info.mode & 0o777) !== 0o600) throw new HookControlsError('write-failed', 'hook controls lock must have mode 0600');
}

async function openLockDescriptor(path: string) {
  let handle: Awaited<ReturnType<typeof open>> | undefined;
  try {
    try {
      handle = await open(path, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | O_NOFOLLOW, 0o600);
    } catch (error) {
      if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
      handle = await open(path, constants.O_RDONLY | O_NOFOLLOW);
    }
    validateLockStat(await handle.stat());
    return handle;
  } catch (error) {
    if (handle) await handle.close();
    if (error instanceof HookControlsError) throw error;
    throw new HookControlsError('write-failed', `unable to open hook controls lock: ${String(error)}`);
  }
}

async function lock(dir: string, mode: 'shared' | 'exclusive'): Promise<() => Promise<void>> {
  await mkdir(dir, { recursive: true, mode: 0o700 });
  await chmod(dir, 0o700);
  const path = join(dir, LOCK_FILE);
  const handle = await openLockDescriptor(path);
  const fd = handle.fd;
  const flockMode = mode === 'shared' ? LOCK_SH : LOCK_EX;
  let acquired = false;
  try {
    const deadline = Date.now() + LOCK_WAIT_MS;
    while (advisoryFlock(fd, flockMode | LOCK_NB) !== 0) {
      if (Date.now() >= deadline) throw new HookControlsError('locked', 'hook controls lock is busy');
      await Bun.sleep(25);
    }
    acquired = true;
    if (lockInitFailure) throw lockInitFailure;
    return async () => {
      let releaseError: unknown;
      try {
        advisoryFlock(fd, LOCK_UN);
        if (lockReleaseFailure) throw lockReleaseFailure;
      } catch (error) {
        releaseError = error;
      } finally {
        await handle.close();
        lockDescriptorCloseCount += 1;
      }
      if (releaseError) throw releaseError;
    };
  } catch (error) {
    if (acquired) advisoryFlock(fd, LOCK_UN);
    await handle.close();
    if (error instanceof HookControlsError) throw error;
    throw new HookControlsError('write-failed', `unable to acquire hook controls lock: ${String(error)}`);
  }
}

async function durableWrite(dir: string, body: string): Promise<HookControlsResponse> {
  const target = join(dir, FILE);
  const temporary = join(dir, `.${FILE}.${process.pid}.${crypto.randomUUID()}.tmp`);
  const written = parse(body);
  let result: HookControlsResponse;
  try {
    await writeFile(temporary, body, { mode: 0o600, flag: 'wx' });
    await chmod(temporary, 0o600);
    await syncFile(temporary);
    await rename(temporary, target);
    try {
      await syncDirectory(dir);
      result = response(written.persisted, written.issue ?? null);
    } catch (error) {
      result = await committedObservedResponse(dir, written, `parent directory fsync failed after commit: ${String(error)}`);
    }
  } catch (error) {
    let cleanupError: unknown;
    try {
      await removeTemp(temporary);
    } catch (cleanupFailure) {
      cleanupError = cleanupFailure;
    }
    const detail = `unable to persist hook controls: ${String(error)}`;
    throw new HookControlsError(
      'write-failed',
      cleanupError === undefined ? detail : `${detail}; temp cleanup failed: ${String(cleanupError)}`,
    );
  }
  try {
    await removeTemp(temporary);
    return result;
  } catch (cleanupError) {
    return markIndeterminate(result, `temp cleanup failed after commit: ${String(cleanupError)}`);
  }
}

async function mutation<T>(dir: string, fn: (state: RawState) => Promise<T>): Promise<T> {
  const release = await lock(dir, 'exclusive');
  let result: T;
  try {
    result = await fn(await readRaw(dir));
  } catch (error) {
    try {
      await release();
    } catch (releaseError) {
      throw releaseError;
    }
    throw error;
  }
  try {
    await release();
    return result;
  } catch (releaseError) {
    if (isHookControlsResponse(result)) {
      return markIndeterminate(result, `lock release failed after commit: ${String(releaseError)}`) as T;
    }
    throw releaseError;
  }
}

export async function setHookControl(id: HookControlId, enabled: boolean, configDirectory?: string): Promise<HookControlsResponse> {
  if (!isRegisteredHookControl(id) || typeof enabled !== 'boolean') throw new HookControlsError('unknown', 'unknown hook control');
  const dir = directory(configDirectory);
  return mutation(dir, async (state) => {
    if (state.issue) throw new HookControlsError('invalid', state.issue.detail);
    const persisted: PersistedHookControls = { version: 'hook-controls/v1', hooks: { ...(state.parsed?.hooks ?? {}), [id]: enabled } };
    return durableWrite(dir, `${JSON.stringify(persisted, null, 2)}\n`);
  });
}

async function removeBackup(path: string): Promise<void> {
  await rmImpl(path, { force: true });
}

async function writeExactBytes(handle: Awaited<ReturnType<typeof open>>, data: Buffer): Promise<void> {
  let offset = 0;
  while (offset < data.length) {
    const { bytesWritten } = await handle.write(data, offset);
    if (bytesWritten === 0) throw new Error('backup write stalled');
    offset += bytesWritten;
  }
}

async function cleanupBackupBeforeCommitFailure(dir: string, backup: string): Promise<string | undefined> {
  let cleanupError: unknown;
  try {
    await removeBackup(backup);
    await syncDirectory(dir);
  } catch (error) {
    cleanupError = error;
  }
  if (cleanupError !== undefined) {
    return `backup cleanup failed: ${String(cleanupError)}`;
  }
  return undefined;
}

export async function repairHookControls(configDirectory?: string): Promise<HookControlsResponse> {
  const dir = directory(configDirectory);
  return mutation(dir, async (state) => {
    if (!state.issue) throw new HookControlsError('invalid', 'hook controls are already valid');
    if (!state.issue.repairable) throw new HookControlsError('invalid', state.issue.detail);
    if (state.raw === null) throw new HookControlsError('invalid', state.issue.detail);
    const backup = join(dir, `${FILE}.corrupt.${Date.now()}.${crypto.randomUUID()}`);
    let handle: Awaited<ReturnType<typeof open>> | undefined;
    try {
      handle = await open(backup, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | O_NOFOLLOW, 0o600);
    } catch (error) {
      throw new HookControlsError('write-failed', `unable to back up corrupt hook controls: ${String(error)}`);
    }
    try {
      if (backupWriteAfterCreateFailure) throw backupWriteAfterCreateFailure;
      await writeExactBytes(handle, state.raw);
      if (backupSyncFailure) throw backupSyncFailure;
      await handle.sync();
      await handle.close();
      handle = undefined;
      await syncDirectory(dir);
      const persisted: PersistedHookControls = { version: 'hook-controls/v1', hooks: {} };
      return await durableWrite(dir, `${JSON.stringify(persisted, null, 2)}\n`);
    } catch (error) {
      if (handle !== undefined) {
        try { await handle.close(); } catch { /* best-effort */ }
      }
      const cleanupDetail = await cleanupBackupBeforeCommitFailure(dir, backup);
      const detail = error instanceof HookControlsError ? error.message : `unable to persist hook controls: ${String(error)}`;
      if (cleanupDetail !== undefined) {
        throw new HookControlsError('write-failed', `${detail}; ${cleanupDetail}`);
      }
      if (error instanceof HookControlsError) throw error;
      throw new HookControlsError('write-failed', detail);
    }
  });
}
