import path from 'path';
import crypto from 'crypto';
import { Workspace, Repository, WorkspaceStore, CrossRepoTask } from './types.js';
import { ConfigManager } from '../../config/config-manager.js';
import { FileSystemAdapter, NodeFileSystemAdapter } from './file-system-adapter.js';

export class WorkspaceDataStore {
  private store: WorkspaceStore = { workspaces: {} };
  private configManager: ConfigManager;
  private readonly MODULE_NAME = 'workspace';
  private readonly DATA_FILE = 'workspace.json';
  private fs: FileSystemAdapter;

  constructor(configManager?: ConfigManager, fs?: FileSystemAdapter) {
    this.configManager = configManager || new ConfigManager();
    this.fs = fs || new NodeFileSystemAdapter();
  }

  async init(): Promise<void> {
    const storageManager = this.configManager.getStorageManager();
    await storageManager.ensureStorageDirectories();
    
    const data = await storageManager.loadData(this.MODULE_NAME, this.DATA_FILE);
    if (data) {
      this.store = data;
    } else {
      await this.save();
    }
  }

  async save(): Promise<void> {
    const storageManager = this.configManager.getStorageManager();
    await storageManager.saveData(this.MODULE_NAME, this.DATA_FILE, this.store);
  }

  createWorkspace(name: string, rootPath: string, description?: string): Workspace {
    const workspace: Workspace = {
      id: this.generateId('ws'),
      name,
      description,
      rootPath,
      repositories: [],
      createdAt: new Date(),
      updatedAt: new Date(),
      settings: {
        autoSync: true,
        syncInterval: 3600000, // 1 hour
        crossRepoTags: true,
      },
    };

    this.store.workspaces[workspace.id] = workspace;
    if (!this.store.activeWorkspaceId) {
      this.store.activeWorkspaceId = workspace.id;
    }
    
    return workspace;
  }

  getWorkspace(nameOrId: string): Workspace | undefined {
    if (this.store.workspaces[nameOrId]) {
      return this.store.workspaces[nameOrId];
    }
    
    return Object.values(this.store.workspaces).find(ws => ws.name === nameOrId);
  }

  getActiveWorkspace(): Workspace | undefined {
    if (!this.store.activeWorkspaceId) {
      return undefined;
    }
    return this.store.workspaces[this.store.activeWorkspaceId];
  }

  setActiveWorkspace(workspaceNameOrId: string): boolean {
    // Try by ID first
    if (this.store.workspaces[workspaceNameOrId]) {
      this.store.activeWorkspaceId = workspaceNameOrId;
      return true;
    }
    
    // Try by name
    const workspace = this.getWorkspace(workspaceNameOrId);
    if (workspace) {
      this.store.activeWorkspaceId = workspace.id;
      return true;
    }
    
    return false;
  }

  addRepository(
    workspaceNameOrId: string,
    repo: Omit<Repository, 'id' | 'lastSync'>
  ): Repository | null {
    const workspace = this.getWorkspace(workspaceNameOrId);
    if (!workspace) {
      return null;
    }

    const newRepo: Repository = {
      ...repo,
      id: this.generateId('repo'),
      lastSync: new Date(),
    };

    workspace.repositories.push(newRepo);
    workspace.updatedAt = new Date();
    
    return newRepo;
  }

  async detectRepositories(rootPath: string): Promise<Repository[]> {
    const repositories: Repository[] = [];
    
    try {
      const entries = await this.fs.readdir(rootPath);
      
      for (const entry of entries) {
        const isDir = typeof entry.isDirectory === 'function' ? entry.isDirectory() : entry.isDirectory;
        if (isDir && !entry.name.startsWith('.')) {
          const repoPath = path.join(rootPath, entry.name);
          
          // Check if it's a git repository
          try {
            await this.fs.access(path.join(repoPath, '.git'));
            
            repositories.push({
              id: this.generateId('repo'),
              name: entry.name,
              path: repoPath,
              type: 'git',
              lastSync: new Date(),
            });
          } catch {
            // Check if it has package.json (might be a workspace package)
            try {
              await this.fs.access(path.join(repoPath, 'package.json'));
              repositories.push({
                id: this.generateId('repo'),
                name: entry.name,
                path: repoPath,
                type: 'local',
                lastSync: new Date(),
              });
            } catch {
              // Not a repository
            }
          }
        }
      }
    } catch (error) {
      console.error('Error detecting repositories:', error);
    }
    
    return repositories;
  }

  getRepositoriesForWorkspace(workspaceNameOrId: string): Repository[] {
    const workspace = this.getWorkspace(workspaceNameOrId);
    return workspace?.repositories || [];
  }

  updateRepository(
    workspaceNameOrId: string,
    repoId: string,
    updates: Partial<Omit<Repository, 'id'>>
  ): boolean {
    const workspace = this.getWorkspace(workspaceNameOrId);
    if (!workspace) {
      return false;
    }

    const repo = workspace.repositories.find(r => r.id === repoId);
    if (!repo) {
      return false;
    }

    Object.assign(repo, updates);
    repo.lastSync = new Date();
    workspace.updatedAt = new Date();
    
    return true;
  }

  getWorkspaces(): Workspace[] {
    return Object.values(this.store.workspaces);
  }

  findRepository(name: string): Repository | undefined {
    for (const workspace of Object.values(this.store.workspaces)) {
      const repo = workspace.repositories.find(r => r.name === name);
      if (repo) {
        return repo;
      }
    }
    return undefined;
  }

  findRepositoryByPath(repoPath: string): Repository | undefined {
    for (const workspace of Object.values(this.store.workspaces)) {
      const repo = workspace.repositories.find(r => r.path === repoPath);
      if (repo) {
        return repo;
      }
    }
    return undefined;
  }

  getPrimaryRepository(): Repository | undefined {
    const activeWorkspace = this.getActiveWorkspace();
    if (!activeWorkspace || activeWorkspace.repositories.length === 0) {
      return undefined;
    }
    
    // Return the first repository marked as primary, or the first repository
    return activeWorkspace.repositories.find(r => r.primary) || activeWorkspace.repositories[0];
  }

  private generateId(prefix: string = ''): string {
    let uuid: string;
    try {
      uuid = crypto.randomUUID();
    } catch {
      // Fallback for environments where crypto.randomUUID is not available
      uuid = Date.now().toString(16) + '-' + Math.random().toString(16).substr(2);
    }
    return prefix ? `${prefix}-${uuid}` : uuid;
  }
}