import { promises as fs } from 'fs';

export interface FileSystemAdapter {
  writeFile(path: string, content: string, encoding: string): Promise<void>;
  readFile(path: string, encoding: string): Promise<string>;
  mkdir(path: string, options?: { recursive?: boolean }): Promise<void>;
  access(path: string): Promise<void>;
  readdir(path: string): Promise<string[]>;
}

export class NodeFileSystemAdapter implements FileSystemAdapter {
  private fs = fs;

  async writeFile(path: string, content: string, encoding: string): Promise<void> {
    await this.fs.writeFile(path, content, { encoding: encoding as BufferEncoding });
  }

  async readFile(path: string, encoding: string): Promise<string> {
    return await this.fs.readFile(path, { encoding: encoding as BufferEncoding });
  }

  async mkdir(path: string, options?: { recursive?: boolean }): Promise<void> {
    await this.fs.mkdir(path, options);
  }

  async access(path: string): Promise<void> {
    await this.fs.access(path);
  }

  async readdir(path: string): Promise<string[]> {
    return await this.fs.readdir(path);
  }
}

export class InMemoryFileSystemAdapter implements FileSystemAdapter {
  private files: Map<string, string> = new Map();
  private directories: Set<string> = new Set();

  async writeFile(path: string, content: string, encoding: string): Promise<void> {
    const dir = path.substring(0, path.lastIndexOf('/'));
    if (dir && !this.directories.has(dir)) {
      throw new Error(`ENOENT: no such file or directory, open '${path}'`);
    }
    this.files.set(path, content);
  }

  async readFile(path: string, encoding: string): Promise<string> {
    const content = this.files.get(path);
    if (content === undefined) {
      throw new Error(`ENOENT: no such file or directory, open '${path}'`);
    }
    return content;
  }

  async mkdir(path: string, options?: { recursive?: boolean }): Promise<void> {
    if (options?.recursive) {
      const parts = path.split('/').filter(p => p);
      let currentPath = '';
      for (const part of parts) {
        currentPath = currentPath ? `${currentPath}/${part}` : `/${part}`;
        this.directories.add(currentPath);
      }
    } else {
      this.directories.add(path);
    }
  }

  async access(path: string): Promise<void> {
    if (!this.files.has(path) && !this.directories.has(path)) {
      throw new Error(`ENOENT: no such file or directory, access '${path}'`);
    }
  }

  async readdir(path: string): Promise<string[]> {
    if (!this.directories.has(path)) {
      throw new Error(`ENOENT: no such file or directory, scandir '${path}'`);
    }
    
    const files: string[] = [];
    for (const [filePath] of this.files) {
      const dir = filePath.substring(0, filePath.lastIndexOf('/'));
      if (dir === path) {
        const fileName = filePath.substring(filePath.lastIndexOf('/') + 1);
        files.push(fileName);
      }
    }
    return files;
  }

  // Test helper methods
  clear(): void {
    this.files.clear();
    this.directories.clear();
  }

  hasFile(path: string): boolean {
    return this.files.has(path);
  }

  hasDirectory(path: string): boolean {
    return this.directories.has(path);
  }

  getFileContent(path: string): string | undefined {
    return this.files.get(path);
  }
}