import {
  IReactiveFileSystem,
  FileEntry,
  FileStat,
  FileSystemEventPayloads,
  FileSystemEvents,
  FSEvent,
  Disposable,
  TypedEventEmitter,
  withEvents,
} from "@firesystem/core";
import type {
  Project,
  ProjectConfig,
  ProjectSource,
  ProjectState,
  WorkspaceConfig,
  WorkspaceSettings,
  WorkspaceEventPayloads,
  WorkspaceStats,
  ProjectMetrics,
  SyncOptions,
  ProjectDiff,
  OptimizationReport,
} from "./types";

/**
 * Options for deleting a project
 */
export interface DeleteProjectOptions {
  deleteData?: boolean;
  skipConfirmation?: boolean;
}
import type { SourceProvider, ProviderRegistry } from "./interfaces/SourceProvider";
// Source loaders removed - will use provider pattern instead
import { WorkspaceDatabase, StoredProject, WorkspaceState } from "./WorkspaceDatabase";
import { WorkspaceImporter } from "./import-export/WorkspaceImporter";
import { WorkspaceExporter, ExportOptions } from "./import-export/WorkspaceExporter";
import { 
  CredentialManager, 
  BrowserCredentialProvider, 
  EnvCredentialProvider 
} from "./credentials/CredentialManager";
import { SourceConfigBuilder } from "./credentials/SourceConfigBuilder";

/**
 * Multi-project workspace manager for Firesystem
 */
export class WorkspaceFileSystem implements IReactiveFileSystem, ProviderRegistry {
  private projects = new Map<string, Project>();
  private activeProjectId: string | null = null;
  private providers = new Map<string, SourceProvider>();
  private database: WorkspaceDatabase;
  private credentialManager: CredentialManager;
  private recentProjectIds: string[] = [];
  private autoDisableTimers = new Map<string, NodeJS.Timeout>();
  private settings: WorkspaceSettings = {
    maxActiveProjects: 10,
    autoDisableAfter: 30 * 60 * 1000, // 30 minutes
    keepFocusedActive: true,
    autoSave: false,
    autoSaveInterval: 60000, // 1 minute
    memoryThreshold: 500 * 1024 * 1024, // 500MB
  };

  /**
   * Event system for workspace events
   */
  public readonly events = new TypedEventEmitter<WorkspaceEventPayloads>();

  constructor(config?: WorkspaceConfig) {
    // Initialize database
    this.database = new WorkspaceDatabase();
    
    // Initialize credential manager
    this.credentialManager = new CredentialManager();
    this.setupCredentialProviders();
    
    // Providers are now registered by the user, not built-in

    // Apply settings if provided
    if (config?.settings) {
      this.settings = { ...this.settings, ...config.settings };
    }
  }

  /**
   * Initialize workspace with optional config
   */
  async initialize(config?: WorkspaceConfig): Promise<void> {
    this.events.emit("workspace:initializing", undefined);

    // Open database
    await this.database.open();

    // Try to restore previous state
    const restored = await this.restoreWorkspaceState();

    if (config) {
      // Load projects from config
      for (const projectConfig of config.projects) {
        await this.loadProject(projectConfig);
      }

      // Set active project
      if (config.activeProjectId) {
        await this.setActiveProject(config.activeProjectId);
      }
    }
    // No default project creation - workspace starts empty

    this.events.emit("workspace:initialized", {
      projectCount: this.projects.size,
    });
  }

  /**
   * Load a project into the workspace
   */
  async loadProject(config: ProjectConfig): Promise<Project> {
    this.events.emit("project:loading", { projectId: config.id });

    try {
      // Check if project already exists
      if (this.projects.has(config.id)) {
        throw new Error(`Project ${config.id} already loaded`);
      }

      // Get provider for this source type first
      const provider = this.getProvider(config.source.type);
      if (!provider) {
        throw new Error(`No provider registered for type: ${config.source.type}`);
      }

      // Validate configuration using provider's validator
      if (provider.validateConfiguration) {
        const validation = await provider.validateConfiguration(config.source.config);
        if (!validation.valid) {
          throw new Error(`Invalid source config: ${validation.errors?.join(", ") || "Validation failed"}`);
        }
      }
      // Note: If provider doesn't have validateConfiguration, we trust it can handle any config

      // Get credentials for this source
      const credentials = await this.credentialManager.getCredentials(config.id, config.source);

      // Merge credentials with source config
      const sourceWithCredentials = {
        ...config.source,
        config: {
          ...config.source.config,
          ...credentials
        }
      };

      // Create file system using provider
      const fs = await provider.createFileSystem(sourceWithCredentials.config);

      // Create project
      const project: Project = {
        id: config.id,
        name: config.name,
        source: config.source,
        fs,
        metadata: {
          created: new Date(),
          modified: new Date(),
          lastOpened: new Date(),
          ...config.metadata,
        },
        state: "loaded",
        lastAccessed: new Date(),
        accessCount: 0,
        memoryUsage: await this.estimateProjectMemoryUsage(fs),
      };

      // Setup event forwarding
      this.setupEventForwarding(project);

      // Store project
      this.projects.set(project.id, project);

      // Save to database
      const storedProject: StoredProject = {
        id: project.id,
        name: project.name,
        source: project.source,
        metadata: project.metadata,
        lastAccessed: new Date()
      };
      await this.database.saveProject(storedProject);

      // Emit loaded event
      this.events.emit("project:loaded", { project });

      // If no active project, set this as active
      if (!this.activeProjectId) {
        await this.setActiveProject(project.id);
      }

      return project;
    } catch (error) {
      this.events.emit("project:error", {
        projectId: config.id,
        error: error as Error,
      });
      throw error;
    }
  }

  /**
   * Unload a project from the workspace
   */
  async unloadProject(projectId: string): Promise<void> {
    const project = this.projects.get(projectId);
    if (!project) {
      throw new Error(`Project ${projectId} not found`);
    }

    this.events.emit("project:unloading", { projectId });

    // If this is the active project, deactivate it
    if (this.activeProjectId === projectId) {
      this.activeProjectId = null;
      this.events.emit("project:deactivated", { projectId });
    }

    // Clean up event listeners
    if (project.fs.events) {
      // Remove all listeners (implementation would need cleanup method)
    }

    // Remove from projects map
    this.projects.delete(projectId);

    this.events.emit("project:unloaded", { projectId });
  }

  /**
   * Get a project by ID
   */
  getProject(projectId: string): Project | null {
    return this.projects.get(projectId) || null;
  }

  /**
   * Get all loaded projects
   */
  getProjects(): Project[] {
    return Array.from(this.projects.values());
  }

  /**
   * Set the active project
   */
  async setActiveProject(projectId: string): Promise<void> {
    const project = this.projects.get(projectId);
    if (!project) {
      throw new Error(`Project ${projectId} not found`);
    }

    const previousId = this.activeProjectId;
    this.activeProjectId = projectId;

    // Update recent projects
    this.recentProjectIds = [projectId, ...this.recentProjectIds.filter(id => id !== projectId)].slice(0, 10);

    // Update database
    await this.database.touchProject(projectId);
    await this.saveWorkspaceState();

    this.events.emit("project:activated", { projectId, previousId: previousId || undefined });
  }

  /**
   * Get the active project
   */
  getActiveProject(): Project | null {
    if (!this.activeProjectId) return null;
    return this.projects.get(this.activeProjectId) || null;
  }

  /**
   * Disable a project (unload but keep configuration)
   */
  async disableProject(projectId: string): Promise<void> {
    const project = this.projects.get(projectId);
    if (!project) return;

    // Don't disable the focused project if configured
    if (this.settings.keepFocusedActive && this.activeProjectId === projectId) {
      throw new Error("Cannot disable the focused project when keepFocusedActive is true");
    }

    this.events.emit("project:disabling", { projectId });

    // Store provider info before unloading
    const provider = this.getProvider(project.source.type);
    const hasLocalData = provider?.hasLocalData?.() || false;

    // Check if this is the active project
    const wasActive = this.activeProjectId === projectId;

    // 1. Just unload from memory
    await this.unloadProject(projectId);

    // 2. Mark as disabled in database
    await this.database.updateProjectState(projectId, { 
      enabled: false,
      disabledAt: new Date()
    });

    // 3. If this was the active project, switch to another one
    if (wasActive) {
      const remainingProjects = Array.from(this.projects.keys());
      if (remainingProjects.length > 0) {
        await this.setActiveProject(remainingProjects[0]);
      }
    }

    // 4. Emit event with useful information
    this.events.emit("project:disabled", { 
      projectId,
      hasLocalData,
      reason: "manual"
    });
  }

  /**
   * Enable a disabled project
   */
  async enableProject(projectId: string): Promise<void> {
    // If project is already enabled, return silently
    if (this.projects.has(projectId)) {
      return;
    }
    
    const storedProject = await this.database.getProject(projectId);
    if (!storedProject) {
      throw new Error(`Project ${projectId} not found in database`);
    }

    // 1. Recreate filesystem via provider
    const provider = this.getProvider(storedProject.source.type);
    if (!provider) {
      throw new Error(`Provider ${storedProject.source.type} not registered`);
    }

    const fs = await provider.createFileSystem(storedProject.source.config);

    // 2. Reload in memory
    const project: Project = {
      id: storedProject.id,
      name: storedProject.name,
      source: storedProject.source,
      fs,
      metadata: storedProject.metadata || {},
      state: "loaded" as ProjectState,
      lastAccessed: new Date(),
      accessCount: 0,
      memoryUsage: 0
    };

    this.projects.set(projectId, project);

    // 3. Update state in database
    await this.database.updateProjectState(projectId, { 
      enabled: true,
      enabledAt: new Date()
    });

    this.events.emit("project:enabled", { projectId });
  }

  /**
   * Batch disable projects
   */
  async disableProjects(projectIds: string[]): Promise<void> {
    for (const projectId of projectIds) {
      await this.disableProject(projectId);
    }
  }

  /**
   * Batch enable projects
   */
  async enableProjects(projectIds: string[]): Promise<void> {
    for (const projectId of projectIds) {
      await this.enableProject(projectId);
    }
  }

  /**
   * Get disabled projects
   */
  async getDisabledProjects(): Promise<StoredProject[]> {
    const allProjects = await this.database.listProjects();
    return allProjects.filter(p => p.enabled === false);
  }

  /**
   * Check if project is enabled
   */
  isProjectEnabled(projectId: string): boolean {
    return this.projects.has(projectId);
  }

  /**
   * Copy files between projects
   */
  async copyFiles(
    sourceId: string,
    pattern: string,
    targetId: string,
    targetPath: string
  ): Promise<void> {
    const sourceProject = this.projects.get(sourceId);
    const targetProject = this.projects.get(targetId);

    if (!sourceProject) throw new Error(`Source project ${sourceId} not found`);
    if (!targetProject) throw new Error(`Target project ${targetId} not found`);

    const files = await sourceProject.fs.glob(pattern);
    let copied = 0;

    for (const file of files) {
      const stat = await sourceProject.fs.stat(file);
      if (stat.type === "file") {
        const content = await sourceProject.fs.readFile(file);
        const targetFilePath = (targetPath + file).replace(/\/+/g, '/');
        await targetProject.fs.writeFile(targetFilePath, content.content, content.metadata);
        copied++;
      }
    }

    this.events.emit("project:files-copied", { 
      sourceId, 
      targetId, 
      fileCount: copied 
    });
  }

  /**
   * Sync projects
   */
  async syncProjects(
    sourceId: string,
    targetId: string,
    options: SyncOptions = {}
  ): Promise<void> {
    const sourceProject = this.projects.get(sourceId);
    const targetProject = this.projects.get(targetId);

    if (!sourceProject) throw new Error(`Source project ${sourceId} not found`);
    if (!targetProject) throw new Error(`Target project ${targetId} not found`);

    const files = await sourceProject.fs.glob("**/*");
    const total = files.length;
    let copied = 0;

    for (const file of files) {
      if (options.filter && !options.filter(file)) continue;

      const stat = await sourceProject.fs.stat(file);
      if (stat.type === "file") {
        const exists = await targetProject.fs.exists(file);
        if (!exists || options.overwrite) {
          const content = await sourceProject.fs.readFile(file);
          await targetProject.fs.writeFile(file, content.content, content.metadata);
          copied++;
          
          if (options.progress) {
            options.progress(copied, total);
          }
        }
      }
    }
  }

  /**
   * Compare projects
   */
  async compareProjects(projectId1: string, projectId2: string): Promise<ProjectDiff> {
    const project1 = this.projects.get(projectId1);
    const project2 = this.projects.get(projectId2);

    if (!project1) throw new Error(`Project ${projectId1} not found`);
    if (!project2) throw new Error(`Project ${projectId2} not found`);

    const files1 = new Set(await project1.fs.glob("**/*"));
    const files2 = new Set(await project2.fs.glob("**/*"));

    const diff: ProjectDiff = {
      added: [],
      modified: [],
      deleted: [],
      unchanged: [],
    };

    // Files in project2 but not in project1 (added)
    for (const file of files2) {
      if (!files1.has(file)) {
        diff.added.push(file);
      }
    }

    // Files in project1 but not in project2 (deleted)
    for (const file of files1) {
      if (!files2.has(file)) {
        diff.deleted.push(file);
      }
    }

    // Files in both - check if modified
    for (const file of files1) {
      if (files2.has(file)) {
        const stat1 = await project1.fs.stat(file);
        const stat2 = await project2.fs.stat(file);
        
        if (stat1.type === "file" && stat2.type === "file") {
          const content1 = await project1.fs.readFile(file);
          const content2 = await project2.fs.readFile(file);
          
          if (content1.content !== content2.content) {
            diff.modified.push(file);
          } else {
            diff.unchanged.push(file);
          }
        }
      }
    }

    return diff;
  }

  /**
   * Get workspace statistics
   */
  async getProjectStats(): Promise<WorkspaceStats> {
    const allProjects = await this.database.listProjects();
    const connections: Record<string, number> = {};

    // Count connections by type
    for (const project of this.projects.values()) {
      const type = project.source.type;
      connections[type] = (connections[type] || 0) + 1;
    }

    // Calculate total memory usage
    let totalMemory = 0;
    for (const project of this.projects.values()) {
      totalMemory += project.memoryUsage || 0;
    }

    const disabledCount = allProjects.filter(p => p.enabled === false).length;

    return {
      total: allProjects.length,
      active: this.projects.size,
      disabled: disabledCount,
      focused: this.activeProjectId,
      memoryUsage: this.formatBytes(totalMemory),
      connections,
    };
  }

  /**
   * Get project metrics
   */
  async getProjectMetrics(projectId: string): Promise<ProjectMetrics> {
    const project = this.projects.get(projectId);
    if (!project) throw new Error(`Project ${projectId} not found`);

    const files = await project.fs.glob("**/*");
    let totalSize = 0;
    let fileCount = 0;
    let largestFile = { path: "", size: 0 };
    let lastModified = new Date(0);

    for (const file of files) {
      const stat = await project.fs.stat(file);
      if (stat.type === "file") {
        fileCount++;
        totalSize += stat.size;
        
        if (stat.size > largestFile.size) {
          largestFile = { path: file, size: stat.size };
        }
        
        if (stat.modified > lastModified) {
          lastModified = stat.modified;
        }
      }
    }

    return {
      fileCount,
      totalSize,
      lastModified,
      accessCount: project.accessCount,
      averageFileSize: fileCount > 0 ? Math.round(totalSize / fileCount) : 0,
      largestFile,
    };
  }

  /**
   * Optimize memory usage
   */
  async optimizeMemoryUsage(): Promise<OptimizationReport> {
    const report: OptimizationReport = {
      projectsDisabled: [],
      memoryFreed: 0,
      connectionsReleased: 0,
    };

    // Get current memory usage
    const stats = await this.getProjectStats();
    const currentMemory = await this.calculateTotalMemoryUsage();

    if (currentMemory < (this.settings.memoryThreshold || Infinity)) {
      return report; // No optimization needed
    }

    // Sort projects by last accessed (oldest first)
    const projectList = Array.from(this.projects.values())
      .filter(p => p.id !== this.activeProjectId) // Never disable active project
      .sort((a, b) => a.lastAccessed.getTime() - b.lastAccessed.getTime());

    // Disable projects until under threshold
    for (const project of projectList) {
      if (currentMemory - report.memoryFreed < (this.settings.memoryThreshold || Infinity)) {
        break;
      }

      const memoryBefore = project.memoryUsage || 0;
      await this.disableProject(project.id);
      
      report.projectsDisabled.push(project.id);
      report.memoryFreed += memoryBefore;
      report.connectionsReleased++;
    }

    return report;
  }

  /**
   * Get active project's file system (for proxying)
   */
  private getActiveFS(): IReactiveFileSystem {
    const project = this.getActiveProject();
    if (!project) {
      throw new Error("No active project");
    }
    
    // Track access
    project.lastAccessed = new Date();
    project.accessCount++;
    this.resetAutoDisableTimer(project.id);
    
    return project.fs;
  }

  /**
   * Reset auto-disable timer for a project
   */
  private resetAutoDisableTimer(projectId: string): void {
    // Clear existing timer
    const existingTimer = this.autoDisableTimers.get(projectId);
    if (existingTimer) {
      clearTimeout(existingTimer);
    }

    // Don't set timer for focused project if configured
    if (this.settings.keepFocusedActive && this.activeProjectId === projectId) {
      return;
    }

    // Set new timer if auto-disable is configured
    if (this.settings.autoDisableAfter && this.settings.autoDisableAfter > 0) {
      const timer = setTimeout(() => {
        this.disableProject(projectId).catch(error => {
          console.error(`Failed to auto-disable project ${projectId}:`, error);
        });
      }, this.settings.autoDisableAfter);

      this.autoDisableTimers.set(projectId, timer);
    }
  }

  /**
   * Estimate project memory usage
   */
  private async estimateProjectMemoryUsage(fs: IReactiveFileSystem): Promise<number> {
    try {
      // Basic estimation - can be improved based on FS type
      const size = await fs.size();
      // Add overhead for metadata and structures (rough estimate)
      return size * 1.2;
    } catch {
      return 0;
    }
  }

  /**
   * Calculate total memory usage
   */
  private async calculateTotalMemoryUsage(): Promise<number> {
    let total = 0;
    for (const project of this.projects.values()) {
      total += project.memoryUsage || 0;
    }
    return total;
  }

  /**
   * Format bytes to human readable
   */
  private formatBytes(bytes: number): string {
    if (bytes === 0) return "0 B";
    const k = 1024;
    const sizes = ["B", "KB", "MB", "GB", "TB"];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
  }

  // Methods registerSourceLoader and getSourceLoader removed
  // Now using Provider Registry methods above

  /**
   * Setup credential providers
   */
  private setupCredentialProviders(): void {
    // Register default providers
    const isNode = typeof window === "undefined";
    
    if (isNode) {
      // Node.js environment - use environment variables
      this.credentialManager.registerProvider("s3", new EnvCredentialProvider());
      this.credentialManager.registerProvider("github", new EnvCredentialProvider());
      this.credentialManager.registerProvider("api", new EnvCredentialProvider());
    } else {
      // Browser environment - use browser provider
      const browserProvider = new BrowserCredentialProvider();
      this.credentialManager.registerProvider("s3", browserProvider);
      this.credentialManager.registerProvider("github", browserProvider);
      this.credentialManager.registerProvider("api", browserProvider);
    }
  }

  /**
   * Provider Registry Implementation
   */
  registerProvider(provider: SourceProvider): void {
    this.providers.set(provider.scheme, provider);
  }

  unregisterProvider(scheme: string): void {
    this.providers.delete(scheme);
  }

  getProvider(scheme: string): SourceProvider | undefined {
    return this.providers.get(scheme);
  }

  getRegisteredProviders(): SourceProvider[] {
    return Array.from(this.providers.values());
  }

  /**
   * Setup event forwarding from project to workspace
   */
  private setupEventForwarding(project: Project): void {
    if (!project.fs.events) return;

    // Forward file system events with project context
    const events = [
      FileSystemEvents.FILE_READING,
      FileSystemEvents.FILE_READ,
      FileSystemEvents.FILE_WRITING,
      FileSystemEvents.FILE_WRITTEN,
      FileSystemEvents.FILE_DELETING,
      FileSystemEvents.FILE_DELETED,
      FileSystemEvents.DIR_CREATING,
      FileSystemEvents.DIR_CREATED,
      FileSystemEvents.DIR_DELETING,
      FileSystemEvents.DIR_DELETED,
    ];

    events.forEach((event) => {
      project.fs.events!.on(event, (payload: any) => {
        // Map to project-specific event
        const projectEvent =
          `project:${event.toLowerCase().replace(/_/g, ":")}` as keyof WorkspaceEventPayloads;

        // Add projectId to payload
        const enrichedPayload = { projectId: project.id, ...payload };

        // Emit on workspace events
        (this.events as any).emit(projectEvent, enrichedPayload);
      });
    });
  }

  /**
   * Convert a project to a different source
   */
  async convertProject(
    projectId: string,
    targetSource: ProjectSource,
  ): Promise<void> {
    const project = this.getProject(projectId);
    if (!project) {
      throw new Error(`Project ${projectId} not found`);
    }

    this.events.emit("project:converting", { projectId, targetSource });

    // Get target provider
    const provider = this.getProvider(targetSource.type);
    if (!provider) {
      throw new Error(`No provider registered for type: ${targetSource.type}`);
    }

    // Create new file system
    const newFs = await provider.createFileSystem(targetSource.config);

    // Copy all files
    const paths = await project.fs.glob("**/*");
    for (const path of paths) {
      const stat = await project.fs.stat(path);

      if (stat.type === "directory") {
        // Skip root directory
        if (path !== "/") {
          await newFs.mkdir(path, true);
        }
      } else {
        const file = await project.fs.readFile(path);
        await newFs.writeFile(path, file.content, file.metadata);
      }
    }

    // Replace project's file system
    project.fs = newFs;
    project.source = targetSource;

    // Re-setup event forwarding
    this.setupEventForwarding(project);

    this.events.emit("project:converted", { projectId, source: targetSource });
  }

  /**
   * Convert to IndexedDB (convenience method)
   */
  async convertToIndexedDB(projectId: string): Promise<void> {
    await this.convertProject(projectId, {
      type: "indexeddb",
      config: {
        dbName: `firesystem-${projectId}`,
      },
    });
  }

  /**
   * Export workspace configuration
   */
  async export(): Promise<WorkspaceConfig> {
    const projects: ProjectConfig[] = Array.from(this.projects.values()).map(
      (p) => ({
        id: p.id,
        name: p.name,
        source: p.source,
        metadata: p.metadata,
      }),
    );

    return {
      version: "1.0.0",
      projects,
      activeProjectId: this.activeProjectId || undefined,
      settings: this.settings,
    };
  }

  /**
   * Import workspace configuration
   */
  async import(config: WorkspaceConfig): Promise<void> {
    // Clear existing workspace
    await this.clear();

    // Apply settings
    if (config.settings) {
      this.settings = { ...this.settings, ...config.settings };
    }

    // Load projects
    for (const projectConfig of config.projects) {
      await this.loadProject(projectConfig);
    }

    // Set active project
    if (config.activeProjectId) {
      this.setActiveProject(config.activeProjectId);
    }
  }

  /**
   * Clear all projects
   */
  async clear(): Promise<void> {
    this.events.emit("workspace:clearing", undefined);

    // Unload all projects
    const projectIds = Array.from(this.projects.keys());
    for (const projectId of projectIds) {
      await this.unloadProject(projectId);
    }

    // Clear all projects from database
    const allStoredProjects = await this.database.listProjects();
    for (const project of allStoredProjects) {
      await this.database.deleteProject(project.id);
    }

    this.activeProjectId = null;
    this.recentProjectIds = [];

    // Clear workspace state
    await this.saveWorkspaceState();

    this.events.emit("workspace:cleared", undefined);
  }

  // IReactiveFileSystem implementation (proxy to active project)

  async readFile(path: string): Promise<FileEntry> {
    return this.getActiveFS().readFile(path);
  }

  async writeFile(
    path: string,
    content: any,
    metadata?: Record<string, any>,
  ): Promise<FileEntry> {
    return this.getActiveFS().writeFile(path, content, metadata);
  }

  async deleteFile(path: string): Promise<void> {
    return this.getActiveFS().deleteFile(path);
  }

  async mkdir(path: string, recursive?: boolean): Promise<FileEntry> {
    return this.getActiveFS().mkdir(path, recursive);
  }

  async rmdir(path: string, recursive?: boolean): Promise<void> {
    return this.getActiveFS().rmdir(path, recursive);
  }

  async exists(path: string): Promise<boolean> {
    return this.getActiveFS().exists(path);
  }

  async stat(path: string): Promise<FileStat> {
    return this.getActiveFS().stat(path);
  }

  async readDir(path: string): Promise<FileEntry[]> {
    return this.getActiveFS().readDir(path);
  }

  async rename(oldPath: string, newPath: string): Promise<FileEntry> {
    return this.getActiveFS().rename(oldPath, newPath);
  }

  async copy(sourcePath: string, targetPath: string): Promise<FileEntry> {
    return this.getActiveFS().copy(sourcePath, targetPath);
  }

  async move(sourcePaths: string[], targetPath: string): Promise<void> {
    return this.getActiveFS().move(sourcePaths, targetPath);
  }

  async glob(pattern: string): Promise<string[]> {
    return this.getActiveFS().glob(pattern);
  }

  watch(path: string, callback: (event: FSEvent) => void): Disposable {
    return this.getActiveFS().watch(path, callback);
  }

  watchGlob?(pattern: string, callback: (event: FSEvent) => void): Disposable {
    const fs = this.getActiveFS();
    if ('watchGlob' in fs && typeof fs.watchGlob === 'function') {
      return fs.watchGlob(pattern, callback);
    }
    throw new Error('Active file system does not support watchGlob');
  }

  async size(): Promise<number> {
    return this.getActiveFS().size();
  }

  // Delegate permission methods to active FS
  async canModify(path: string): Promise<boolean> {
    return this.getActiveFS().canModify(path);
  }

  async canCreateIn(parentPath: string): Promise<boolean> {
    return this.getActiveFS().canCreateIn(parentPath);
  }

  /**
   * Check if a project exists and can be accessed
   */
  private async isProjectAccessible(projectId: string): Promise<boolean> {
    try {
      const storedProject = await this.database.getProject(projectId);
      if (!storedProject) return false;

      // For IndexedDB projects, also check if the database still exists
      if (storedProject.source.type === "indexeddb") {
        const dbName = storedProject.source.config.dbName;
        if (dbName) {
          try {
            // Check if IndexedDB is available and can list databases
            if (typeof indexedDB === "undefined" || !indexedDB.databases) {
              // Fallback: assume accessible if we can't check
              return true;
            }
            
            // Try to access the project's database
            const databases = await indexedDB.databases();
            const projectDbExists = databases.some(db => db.name === dbName);
            return projectDbExists;
          } catch (error) {
            console.warn(`Failed to check database existence for project ${projectId}:`, error);
            // Fallback: assume accessible if we can't check
            return true;
          }
        }
      }

      // For other project types (memory, api, github, etc), just check if stored
      return true;
    } catch (error) {
      console.warn(`Failed to check project accessibility for ${projectId}:`, error);
      return false;
    }
  }

  // Persistence methods

  /**
   * Restore workspace state from database
   */
  private async restoreWorkspaceState(): Promise<boolean> {
    try {
      // Get stored state
      const state = await this.database.getWorkspaceState();
      if (!state) return false;

      // Restore settings (but constructor settings take precedence)
      if (state.settings) {
        this.settings = { ...state.settings, ...this.settings };
      }

      // Validate and clean orphaned references
      let stateChanged = false;
      
      // Validate activeProjectId
      if (state.activeProjectId) {
        const activeProjectAccessible = await this.isProjectAccessible(state.activeProjectId);
        if (!activeProjectAccessible) {
          console.warn(`Removing orphaned activeProjectId: ${state.activeProjectId}`);
          state.activeProjectId = null;
          stateChanged = true;
        }
      }

      // Validate recentProjectIds
      if (state.recentProjectIds && state.recentProjectIds.length > 0) {
        const validRecentIds: string[] = [];
        for (const projectId of state.recentProjectIds) {
          const projectAccessible = await this.isProjectAccessible(projectId);
          if (projectAccessible) {
            validRecentIds.push(projectId);
          } else {
            console.warn(`Removing orphaned recentProjectId: ${projectId}`);
            stateChanged = true;
          }
        }
        state.recentProjectIds = validRecentIds;
      }

      // Save cleaned state if any orphaned references were found
      if (stateChanged) {
        await this.database.saveWorkspaceState(state);
        console.info("Workspace state cleaned: removed orphaned project references");
      }

      // Restore recent projects
      this.recentProjectIds = state.recentProjectIds || [];

      // Get stored projects
      const storedProjects = await this.database.listProjects();

      // Load active project if still valid
      if (state.activeProjectId) {
        const activeProject = storedProjects.find(p => p.id === state.activeProjectId);
        if (activeProject) {
          try {
            await this.loadProjectFromStored(activeProject);
            this.activeProjectId = state.activeProjectId;
          } catch (error) {
            console.warn("Failed to restore active project:", error);
            // Remove active project reference if it can't be loaded
            this.activeProjectId = null;
            state.activeProjectId = null;
            await this.database.saveWorkspaceState(state);
          }
        }
      }

      return true;
    } catch (error) {
      console.error("Failed to restore workspace state:", error);
      return false;
    }
  }

  /**
   * Save current workspace state to database
   */
  private async saveWorkspaceState(): Promise<void> {
    const state: WorkspaceState = {
      id: "current",
      activeProjectId: this.activeProjectId,
      recentProjectIds: this.recentProjectIds,
      settings: this.settings
    };

    await this.database.saveWorkspaceState(state);
  }

  /**
   * Load a project from stored configuration
   */
  private async loadProjectFromStored(stored: StoredProject): Promise<Project> {
    const config: ProjectConfig = {
      id: stored.id,
      name: stored.name,
      source: stored.source,
      metadata: stored.metadata
    };

    return this.loadProject(config);
  }


  /**
   * List all registered projects from database
   */
  async listStoredProjects(): Promise<StoredProject[]> {
    return this.database.listProjects();
  }

  /**
   * List recent projects
   */
  async listRecentProjects(limit = 10): Promise<StoredProject[]> {
    return this.database.listRecentProjects(limit);
  }

  /**
   * Delete a project from workspace and optionally delete its data
   */
  async deleteProject(projectId: string, options: DeleteProjectOptions = {}): Promise<void> {
    const project = this.projects.get(projectId);
    if (!project && !await this.database.getProject(projectId)) {
      // Project doesn't exist, just return silently
      return;
    }

    // 1. Emit confirmation event if not skipped
    if (!options.skipConfirmation) {
      // Create a cancellable event
      const confirmEvent: any = {
        projectId,
        project: project || await this.database.getProject(projectId),
        cancelled: false
      };
      
      // Emit confirmation event - listeners can set cancelled to true
      this.events.emit("project:delete-confirm", confirmEvent);
      
      // Check if any listener cancelled the deletion
      if (confirmEvent.cancelled) {
        return; // Deletion cancelled
      }
    }

    // 2. If requested, ask provider to delete data
    if (options.deleteData) {
      const projectData = project || await this.database.getProject(projectId);
      if (projectData) {
        const provider = this.getProvider(projectData.source.type);
        
        // Provider decides what to do based on type
        if (provider?.deleteProjectData) {
          try {
            await provider.deleteProjectData(projectData.source.config);
          } catch (error) {
            console.warn(`Failed to delete project data: ${error instanceof Error ? error.message : String(error)}`);
            // Continue with deletion even if data deletion fails
          }
        }
      }
    }

    // 3. Unload if loaded
    if (this.projects.has(projectId)) {
      await this.unloadProject(projectId);
    }

    // 4. Remove from database
    await this.database.deleteProject(projectId);

    // 5. Update state if this was the active project
    if (this.activeProjectId === projectId) {
      this.activeProjectId = null;
      await this.saveWorkspaceState();
    }

    // 6. Emit deleted event
    this.events.emit("project:deleted", { 
      projectId, 
      deletedData: options.deleteData || false 
    });
  }

  /**
   * Discover existing IndexedDB projects
   */
  async discoverIndexedDBProjects(): Promise<ProjectConfig[]> {
    const dbNames = await this.database.discoverIndexedDBProjects();
    
    return dbNames.map(dbName => ({
      id: `discovered-${dbName}`,
      name: dbName.replace(/^(firesystem-|@firesystem\/)/i, ""),
      source: {
        type: "indexeddb",
        config: { dbName }
      }
    }));
  }

  /**
   * Import workspace configuration from URL
   */
  async importFromUrl(url: string): Promise<void> {
    this.events.emit("workspace:importing", { source: url });

    try {
      const config = await WorkspaceImporter.fromJsonUrl(url);
      await this.importWorkspaceConfig(config);

      this.events.emit("workspace:imported", { source: url });
    } catch (error) {
      this.events.emit("workspace:import-failed", { source: url, error });
      throw error;
    }
  }

  /**
   * Import workspace from GitHub Gist
   */
  async importFromGitHubGist(gistId: string, token?: string): Promise<void> {
    this.events.emit("workspace:importing", { source: `gist:${gistId}` });

    try {
      const config = await WorkspaceImporter.fromGitHubGist(gistId, token);
      await this.importWorkspaceConfig(config);

      this.events.emit("workspace:imported", { source: `gist:${gistId}` });
    } catch (error) {
      this.events.emit("workspace:import-failed", { source: `gist:${gistId}`, error });
      throw error;
    }
  }

  /**
   * Import workspace configuration
   */
  private async importWorkspaceConfig(config: WorkspaceConfig): Promise<void> {
    // Apply settings
    if (config.settings) {
      this.settings = { ...this.settings, ...config.settings };
    }

    // Load projects
    for (const projectConfig of config.projects) {
      await this.loadProject(projectConfig);
    }

    // Set active project
    if (config.activeProjectId) {
      await this.setActiveProject(config.activeProjectId);
    }

    // Save state
    await this.saveWorkspaceState();
  }

  /**
   * Export workspace configuration
   */
  async exportWorkspace(options: ExportOptions = {}): Promise<any> {
    const projects = Array.from(this.projects.values());
    const exportData = await WorkspaceExporter.toJson(
      projects,
      this.activeProjectId,
      this.settings,
      options
    );

    return exportData;
  }

  /**
   * Export workspace to GitHub Gist
   */
  async exportToGitHubGist(options: {
    token: string;
    description?: string;
    public?: boolean;
    includeFiles?: boolean;
  }): Promise<string> {
    const exportData = await this.exportWorkspace({
      includeFiles: options.includeFiles
    });

    const gistId = await WorkspaceExporter.toGitHubGist(exportData, options);
    return gistId;
  }

  /**
   * Export workspace to API endpoint
   */
  async exportToApi(url: string, options?: {
    headers?: Record<string, string>;
    method?: string;
    includeFiles?: boolean;
  }): Promise<void> {
    const exportData = await this.exportWorkspace({
      includeFiles: options?.includeFiles
    });

    await WorkspaceExporter.toApi(exportData, url, options);
  }

  // Credential Management

  /**
   * Register a custom credential provider
   */
  registerCredentialProvider(sourceType: string, provider: any): void {
    this.credentialManager.registerProvider(sourceType, provider);
  }

  /**
   * Get credentials for a project (useful for debugging)
   */
  async getProjectCredentials(projectId: string): Promise<any> {
    const stored = await this.database.getProject(projectId);
    if (!stored) throw new Error("Project not found");
    
    return this.credentialManager.getCredentials(projectId, stored.source);
  }

  /**
   * Clear cached credentials
   */
  clearCredentialCache(projectId?: string): void {
    this.credentialManager.clearCache(projectId);
  }

  // Helper methods for creating projects with common patterns

  /**
   * Create a new IndexedDB project
   */
  async createIndexedDBProject(name: string, dbName?: string): Promise<Project> {
    const source = SourceConfigBuilder.indexedDB(dbName);
    const config: ProjectConfig = {
      id: `idb-${Date.now()}`,
      name,
      source
    };

    return this.loadProject(config);
  }

  /**
   * Create a new Memory project
   */
  async createMemoryProject(name: string, initialData?: any): Promise<Project> {
    const source = SourceConfigBuilder.memory(initialData);
    const config: ProjectConfig = {
      id: `mem-${Date.now()}`,
      name,
      source
    };

    return this.loadProject(config);
  }

  /**
   * Create a new S3 project
   */
  async createS3Project(
    name: string, 
    bucket: string, 
    options?: {
      prefix?: string;
      region?: string;
      credentials?: { accessKeyId: string; secretAccessKey: string };
    }
  ): Promise<Project> {
    const source = SourceConfigBuilder.s3({
      bucket,
      prefix: options?.prefix,
      region: options?.region,
      credentials: options?.credentials
    });

    const config: ProjectConfig = {
      id: `s3-${bucket}-${Date.now()}`,
      name,
      source
    };

    return this.loadProject(config);
  }

  /**
   * Create a new GitHub project
   */
  async createGitHubProject(
    name: string,
    owner: string,
    repo: string,
    options?: {
      branch?: string;
      path?: string;
      token?: string;
    }
  ): Promise<Project> {
    const source = SourceConfigBuilder.github({
      owner,
      repo,
      branch: options?.branch,
      path: options?.path,
      token: options?.token
    });

    const config: ProjectConfig = {
      id: `gh-${owner}-${repo}-${Date.now()}`,
      name,
      source
    };

    return this.loadProject(config);
  }

  /**
   * Close workspace and database connection
   */
  async close(): Promise<void> {
    await this.clear();
    this.credentialManager.clearCache();
    this.database.close();
  }
}
