import { access, constants, open, readFile, rm, rmdir } from 'fs/promises';
import { parse } from './json.js';
import { Logger } from './logger.js';
export class FileSystem {
  public static async rm(path: string, options?: { recursive: boolean, force: boolean }): Promise<boolean> {
    try {
      options ??= { recursive: true, force: true };
      await rm(path, options);
      return true;
    } catch (error: any) {
      Logger.error(`Failed to remove ${path}`);
      return false;
    }
  }

  public static async rmdir(path: string, options?: { recursive: boolean, force: boolean }): Promise<boolean> {
    try {
      options ??= { recursive: true, force: true };
      await rmdir(path, options);
      return true;
    } catch (error: any) {
      Logger.error(`Failed to remove ${path}`);
      return false;
    }
  }

  public static async access(path: string): Promise<boolean> {
    try {
      await access(path, constants.R_OK | constants.W_OK);
      return true;
    } catch {
      Logger.error(`Cannot access ${path}`);
      return false;
    }
  }

  public static toJson(data: string): any {
    try {
      return parse(data);
    } catch (error: any) {
      Logger.error(`Failed to parse ${data} toJson`);
      return {};
    }
  }

  public static async exists(path: string): Promise<boolean> {
    try {
      await access(path, constants.F_OK);
      return true;
    } catch (error: any) {
      Logger.warn(`${path} does not exist`);
      return false;
    }
  }

  public static async read(path: string): Promise<Buffer | undefined> {
    try {
      const exists = await FileSystem.exists(path);
      return !exists ? undefined : await readFile(path);
    } catch (error: any) {
      Logger.warn(`Failed to read ${path}`);
      return undefined;
    }
  }

  public static async readToString(path: string): Promise<string> {
    try {
      const data = await FileSystem.read(path);
      if (!data) {
        const touched = await FileSystem.touch(path);
        Logger.info(`File ${path} touched ${touched}`);
        return '';
      }
      return data.toString();
    } catch (error: any) {
      Logger.warn(`Failed to read ${path} toString`);
      return '';
    }
  }

  public static async readToJson(path: string): Promise<any> {
    try {
      const data = await FileSystem.readToString(path);
      if (!data) {
        const touched = await FileSystem.touch(path, {});
        Logger.info(`File ${path} touched ${touched}`);
        return {};
      }
      return FileSystem.toJson(data);
    } catch (error: any) {
      Logger.warn(`Failed to read ${path} toJson`);
      return {};
    }
  }

  public static async touch(path: string, data: any = ''): Promise<boolean> {
    try {
      const file = await open(path, 'w', 0o700);
      await file.writeFile(data);
      await file.close();
      return true;
    } catch (error: any) {
      Logger.warn(`Failed to touch ${path}`);
      return false;
    }
  }

  public static async write(path: string, data: string): Promise<boolean> {
    try {
      const exists = await FileSystem.exists(path);
      if (!exists) {
        const file = await open(path, 'w', 0o700);
        await file.writeFile(data, 'utf-8');
        await file.close();
        return true;
      }
      return false;
    } catch (error: any) {
      Logger.error(`Failed to write ${path}`);
      return false;
    }
  }

  public static async append(path: string, data: string): Promise<boolean> {
    try {
      const file = await open(path, 'a', 0o700);
      await file.appendFile(data, 'utf-8');
      await file.close();
      return true;
    } catch (error: any) {
      Logger.error(`Failed to append ${path}`);
      return false;
    }
  }

  public static async overwrite(path: string, data: string): Promise<boolean> {
    try {
      const file = await open(path, 'w', 0o700);
      await file.writeFile(data, 'utf-8');
      await file.close();
      return true;
    } catch (error: any) {
      Logger.error(`Failed to overwrite ${path}`);
      return false;
    }
  }
}
