import { writeFile, readFile, rename, unlink, mkdir, readdir } from 'fs/promises';
import { existsSync } from 'fs';
import { dirname } from 'path';
import { randomBytes } from 'crypto';
import { createModuleLogger } from '../../utils/logger.js';

const log = createModuleLogger('file-ops');

/**
 * Atomically writes JSON data to a file.
 * Writes to a temp file first, then renames to prevent corruption on crash.
 */
export async function atomicWriteJSON<T>(filePath: string, data: T): Promise<void> {
  const dir = dirname(filePath);
  await ensureDir(dir);

  const tempPath = `${filePath}.${randomBytes(6).toString('hex')}.tmp`;
  const content = JSON.stringify(data, null, 2);

  await writeFile(tempPath, content, 'utf-8');
  await rename(tempPath, filePath);
  log.debug({ filePath }, 'Atomic write completed');
}

/**
 * Safely reads and parses a JSON file.
 * Returns null if the file doesn't exist or contains invalid JSON.
 */
export async function readJSON<T>(filePath: string): Promise<T | null> {
  try {
    if (!existsSync(filePath)) return null;
    const content = await readFile(filePath, 'utf-8');
    return JSON.parse(content) as T;
  } catch (err) {
    log.error({ filePath, err }, 'Failed to read JSON file');
    return null;
  }
}

/**
 * Deletes a file. Returns true if deleted, false if not found or on error.
 */
export async function deleteFile(filePath: string): Promise<boolean> {
  try {
    if (!existsSync(filePath)) return false;
    await unlink(filePath);
    return true;
  } catch (err) {
    log.error({ filePath, err }, 'Failed to delete file');
    return false;
  }
}


/**
 * Ensures a directory exists, creating it recursively if needed.
 */
export async function ensureDir(dirPath: string): Promise<void> {
  await mkdir(dirPath, { recursive: true });
}

/**
 * Synchronously checks if a file exists.
 */
export function fileExists(filePath: string): boolean {
  return existsSync(filePath);
}

/**
 * Lists files in a directory, optionally filtering by extension.
 * Returns an empty array if the directory doesn't exist.
 */
export async function listFiles(dirPath: string, extension?: string): Promise<string[]> {
  try {
    if (!existsSync(dirPath)) return [];
    const entries = await readdir(dirPath);
    if (extension) {
      return entries.filter(e => e.endsWith(extension));
    }
    return entries;
  } catch (err) {
    log.error({ dirPath, err }, 'Failed to list files');
    return [];
  }
}

/**
 * Lists subdirectories in a directory.
 * Returns an empty array if the directory doesn't exist.
 */
export async function listDirectories(dirPath: string): Promise<string[]> {
  try {
    if (!existsSync(dirPath)) return [];
    const entries = await readdir(dirPath, { withFileTypes: true });
    return entries.filter(e => e.isDirectory()).map(e => e.name);
  } catch (err) {
    log.error({ dirPath, err }, 'Failed to list directories');
    return [];
  }
}
