import type { IReactiveFileSystem } from "../interfaces/IReactiveFileSystem";
import type { IFileSystemCapabilities } from "../interfaces/FileSystemCapabilities";
import type { FileEntry, FileStat, FileMetadata } from "../types/FileEntry";
import type { FSEvent } from "../types/Events";
import type { Disposable } from "../types/Common";
import { normalizePath } from "../utils/paths";

/**
 * Classe base abstrata com implementações padrão para FileSystem
 * Fornece implementações padrão para os novos métodos de permissão
 */
export abstract class BaseFileSystem implements IReactiveFileSystem {
  // Métodos abstratos que cada implementação deve fornecer
  abstract get events(): any;
  abstract readFile(path: string): Promise<FileEntry>;
  abstract writeFile(
    path: string,
    content: any,
    metadata?: FileMetadata,
  ): Promise<FileEntry>;
  abstract deleteFile(path: string): Promise<void>;
  abstract exists(path: string): Promise<boolean>;
  abstract readDir(path: string): Promise<FileEntry[]>;
  abstract mkdir(path: string, recursive?: boolean): Promise<FileEntry>;
  abstract rmdir(path: string, recursive?: boolean): Promise<void>;
  abstract rename(oldPath: string, newPath: string): Promise<FileEntry>;
  abstract move(sourcePaths: string[], targetPath: string): Promise<void>;
  abstract copy(sourcePath: string, targetPath: string): Promise<FileEntry>;
  abstract watch(
    pattern: string,
    callback: (event: FSEvent) => void,
  ): Disposable;
  abstract stat(path: string): Promise<FileStat>;
  abstract glob(pattern: string): Promise<string[]>;
  abstract clear(): Promise<void>;
  abstract size(): Promise<number>;

  // Implementações padrão dos novos métodos

  /**
   * Por padrão, permite modificação se o arquivo existe ou se pode ser criado
   */
  async canModify(path: string): Promise<boolean> {
    try {
      const stat = await this.stat(path);
      return !stat.readonly;
    } catch {
      // Se não existe, verifica se pode criar no diretório pai
      const parentPath = this.getParentPath(path);
      if (parentPath === "/") {
        return true; // Root sempre permite criar
      }
      return this.canCreateIn(parentPath);
    }
  }

  /**
   * Por padrão, permite criar se o diretório existe e não é readonly
   */
  async canCreateIn(parentPath: string): Promise<boolean> {
    try {
      const stat = await this.stat(parentPath);
      return stat.type === "directory" && !stat.readonly;
    } catch {
      return false; // Diretório não existe
    }
  }

  /**
   * Implementação padrão de escrita atômica usando temp + rename
   */
  async writeFileAtomic(
    path: string,
    content: any,
    metadata?: FileMetadata,
  ): Promise<FileEntry> {
    const normalizedPath = normalizePath(path);
    const tempPath = `${normalizedPath}.tmp${Date.now()}`;

    try {
      // 1. Escreve no arquivo temporário
      await this.writeFile(tempPath, content, metadata);

      // 2. Remove o original se existir
      if (await this.exists(normalizedPath)) {
        await this.deleteFile(normalizedPath);
      }

      // 3. Renomeia temp para final
      return await this.rename(tempPath, normalizedPath);
    } catch (error) {
      // Cleanup em caso de erro
      try {
        await this.deleteFile(tempPath);
      } catch {
        // Ignora erro no cleanup
      }
      throw error;
    }
  }

  /**
   * Helper para extrair diretório pai
   */
  protected getParentPath(path: string): string {
    const normalized = normalizePath(path);
    const lastSlash = normalized.lastIndexOf("/");
    return lastSlash <= 0 ? "/" : normalized.substring(0, lastSlash);
  }
}
