import type { 
  ProjectConfig, 
  WorkspaceSettings,
  ProjectMetadata 
} from "./types";

const DB_NAME = "@firesystem/workspace";
const DB_VERSION = 1;

export interface StoredProject {
  id: string;
  name: string;
  source: any; // ProjectSource
  metadata: ProjectMetadata;
  lastAccessed: Date;
  enabled?: boolean;
  disabledAt?: Date;
  enabledAt?: Date;
}

export interface WorkspaceState {
  id: "current"; // Single record key
  activeProjectId: string | null;
  recentProjectIds: string[];
  settings: WorkspaceSettings;
}

/**
 * Manages workspace persistence in IndexedDB
 */
export class WorkspaceDatabase {
  private db: IDBDatabase | null = null;
  private dbName: string;
  private dbVersion: number;

  constructor(dbName: string = DB_NAME, dbVersion: number = DB_VERSION) {
    this.dbName = dbName;
    this.dbVersion = dbVersion;
  }

  /**
   * Open or create the workspace database
   */
  async open(): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.dbVersion);

      request.onerror = () => {
        reject(new Error(`Failed to open database: ${request.error}`));
      };

      request.onsuccess = () => {
        this.db = request.result;
        resolve();
      };

      request.onupgradeneeded = (event) => {
        const db = request.result;
        const oldVersion = event.oldVersion;

        // Create object stores for v1
        if (oldVersion < 1) {
          // Projects store
          if (!db.objectStoreNames.contains("projects")) {
            const projectStore = db.createObjectStore("projects", { 
              keyPath: "id" 
            });
            projectStore.createIndex("name", "name", { unique: false });
            projectStore.createIndex("lastAccessed", "lastAccessed", { unique: false });
          }

          // Workspace state store
          if (!db.objectStoreNames.contains("workspaceState")) {
            db.createObjectStore("workspaceState", { keyPath: "id" });
          }
        }
      };
    });
  }

  /**
   * Ensure database is open
   */
  private ensureOpen(): void {
    if (!this.db) {
      throw new Error("Database not opened. Call open() first.");
    }
  }

  /**
   * Close the database
   */
  close(): void {
    if (this.db) {
      this.db.close();
      this.db = null;
    }
  }

  // Project CRUD operations

  /**
   * Save or update a project configuration
   */
  async saveProject(project: StoredProject): Promise<void> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["projects"], "readwrite");
    const store = tx.objectStore("projects");
    
    await new Promise<void>((resolve, reject) => {
      const request = store.put(project);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * Get a project by ID
   */
  async getProject(id: string): Promise<StoredProject | null> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["projects"], "readonly");
    const store = tx.objectStore("projects");
    
    return new Promise((resolve, reject) => {
      const request = store.get(id);
      request.onsuccess = () => resolve(request.result || null);
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * List all projects
   */
  async listProjects(): Promise<StoredProject[]> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["projects"], "readonly");
    const store = tx.objectStore("projects");
    
    return new Promise((resolve, reject) => {
      const request = store.getAll();
      request.onsuccess = () => resolve(request.result || []);
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * List recent projects
   */
  async listRecentProjects(limit = 10): Promise<StoredProject[]> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["projects"], "readonly");
    const index = tx.objectStore("projects").index("lastAccessed");
    
    const projects: StoredProject[] = [];
    
    return new Promise((resolve, reject) => {
      // Open cursor in reverse order (most recent first)
      const request = index.openCursor(null, "prev");
      
      request.onsuccess = () => {
        const cursor = request.result;
        if (cursor && projects.length < limit) {
          projects.push(cursor.value);
          cursor.continue();
        } else {
          resolve(projects);
        }
      };
      
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * Delete a project
   */
  async deleteProject(id: string): Promise<void> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["projects"], "readwrite");
    const store = tx.objectStore("projects");
    
    await new Promise<void>((resolve, reject) => {
      const request = store.delete(id);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * Update project last accessed time
   */
  async touchProject(id: string): Promise<void> {
    const project = await this.getProject(id);
    if (project) {
      project.lastAccessed = new Date();
      await this.saveProject(project);
    }
  }

  /**
   * Update project state
   */
  async updateProjectState(id: string, updates: Partial<Pick<StoredProject, 'enabled' | 'disabledAt' | 'enabledAt'>>): Promise<void> {
    const project = await this.getProject(id);
    if (project) {
      // Apply updates
      Object.assign(project, updates);
      await this.saveProject(project);
    }
  }

  // Workspace state operations

  /**
   * Save workspace state
   */
  async saveWorkspaceState(state: WorkspaceState): Promise<void> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["workspaceState"], "readwrite");
    const store = tx.objectStore("workspaceState");
    
    await new Promise<void>((resolve, reject) => {
      const request = store.put(state);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * Get workspace state
   */
  async getWorkspaceState(): Promise<WorkspaceState | null> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["workspaceState"], "readonly");
    const store = tx.objectStore("workspaceState");
    
    return new Promise((resolve, reject) => {
      const request = store.get("current");
      request.onsuccess = () => resolve(request.result || null);
      request.onerror = () => reject(request.error);
    });
  }

  /**
   * Clear all data
   */
  async clear(): Promise<void> {
    this.ensureOpen();
    
    const tx = this.db!.transaction(["projects", "workspaceState"], "readwrite");
    
    await Promise.all([
      new Promise<void>((resolve, reject) => {
        const request = tx.objectStore("projects").clear();
        request.onsuccess = () => resolve();
        request.onerror = () => reject(request.error);
      }),
      new Promise<void>((resolve, reject) => {
        const request = tx.objectStore("workspaceState").clear();
        request.onsuccess = () => resolve();
        request.onerror = () => reject(request.error);
      })
    ]);
  }

  /**
   * Check if a project exists by checking IndexedDB databases
   */
  static async projectDatabaseExists(dbName: string): Promise<boolean> {
    if (!("databases" in indexedDB)) {
      // Fallback for browsers that don't support databases()
      return new Promise((resolve) => {
        const testOpen = indexedDB.open(dbName);
        testOpen.onsuccess = () => {
          const db = testOpen.result;
          const exists = db.version > 0;
          db.close();
          resolve(exists);
        };
        testOpen.onerror = () => resolve(false);
      });
    }

    const databases = await indexedDB.databases();
    return databases.some(db => db.name === dbName);
  }

  /**
   * Discover existing Firesystem IndexedDB databases
   */
  async discoverIndexedDBProjects(): Promise<string[]> {
    if (!("databases" in indexedDB)) {
      console.warn("IndexedDB.databases() not supported in this browser");
      return [];
    }

    const databases = await indexedDB.databases();
    
    // Filter databases that look like Firesystem projects
    return databases
      .filter(db => {
        const name = db.name || "";
        return name.startsWith("firesystem-") || 
               name.startsWith("@firesystem/") ||
               name.includes("-filesystem");
      })
      .map(db => db.name!);
  }
}