/**
 * WorkspaceManager - Create/merge/cleanup workspaces
 *
 * Implements DD §7.5. Mechanism owner only — never plans.
 * INV-1: workspaces.status only via workspace.* event projection.
 *
 * @module packages/runtime/src/scheduler/WorkspaceManager
 */

import { mkdirSync, existsSync, rmSync } from 'fs'
import { join } from 'path'

export type WorkspaceStrategy = 'main' | 'worktree' | 'isolated_copy'

export interface Workspace {
  id: string
  path: string
  strategy: WorkspaceStrategy
  state: 'active' | 'merged' | 'abandoned' | 'cleaned'
  created_at: string
  merged_at?: string
  task_id?: string
}

export class WorkspaceManager {
  private workspaces: Map<string, Workspace> = new Map()
  private project_root: string

  constructor(project_root: string) {
    this.project_root = project_root
  }

  /**
   * Create a new workspace.
   */
  create_workspace(
    task_id: string,
    strategy: WorkspaceStrategy = 'isolated_copy'
  ): Workspace {
    const workspace_id = `ws_${task_id}_${Date.now()}`
    const path = join(this.project_root, '.air', 'workspaces', workspace_id)

    // Create workspace directory
    if (!existsSync(path)) {
      mkdirSync(path, { recursive: true })
    }

    const ws: Workspace = {
      id: workspace_id,
      path,
      strategy,
      state: 'active',
      created_at: new Date().toISOString(),
      task_id
    }

    this.workspaces.set(workspace_id, ws)

    // INV-1: Emit workspace.created event instead of writing status directly
    // EventStore.append('workspace.created', { workspace_id, ... })
    // State is tracked in-memory only; persistent status via event projection

    return ws
  }

  /**
   * Merge workspace back to main.
   * INV-1: Status transition via workspace.merged event, not direct write.
   */
  async merge_workspace(workspace_id: string): Promise<{ ok: boolean; conflict: boolean; message: string }> {
    const ws = this.workspaces.get(workspace_id)
    if (!ws) return { ok: false, conflict: false, message: 'Unknown workspace' }
    if (ws.state !== 'active') return { ok: false, conflict: false, message: `Workspace is ${ws.state}` }

    try {
      // Merge logic would use git merge for worktree strategy
      // Only update in-memory state after successful merge
      ws.state = 'merged'
      ws.merged_at = new Date().toISOString()
      // INV-1: Emit workspace.merged event for projection to update persistent status

      return { ok: true, conflict: false, message: 'Merged successfully' }
    } catch (error) {
      return { ok: false, conflict: true, message: error instanceof Error ? error.message : 'Merge failed' }
    }
  }

  /**
   * Cleanup workspace (GC).
   * INV-1: Status transition via workspace.cleaned event, not direct write.
   */
  cleanup_workspace(workspace_id: string): { ok: boolean; message: string } {
    const ws = this.workspaces.get(workspace_id)
    if (!ws) return { ok: false, message: 'Unknown workspace' }
    if (ws.state === 'cleaned') return { ok: false, message: 'Already cleaned' }

    try {
      if (existsSync(ws.path)) {
        rmSync(ws.path, { recursive: true, force: true })
      }
      ws.state = 'cleaned'
      // INV-1: Emit workspace.cleaned event for projection

      return { ok: true, message: 'Cleaned' }
    } catch (error) {
      return { ok: false, message: error instanceof Error ? error.message : 'Cleanup failed' }
    }
  }

  /**
   * Get all active workspaces.
   */
  get_active(): Workspace[] {
    return Array.from(this.workspaces.values()).filter(ws => ws.state === 'active')
  }

  /**
   * GC scan — find workspaces eligible for cleanup.
   */
  gc_scan(): Workspace[] {
    const now = Date.now()
    const eligible: Workspace[] = []

    for (const ws of this.workspaces.values()) {
      const age = now - new Date(ws.created_at).getTime()
      const age_days = age / (1000 * 60 * 60 * 24)

      if (ws.state === 'abandoned' && age_days > 3) {
        eligible.push(ws)
      } else if (ws.state === 'merged' && age_days > 7) {
        eligible.push(ws)
      }
    }

    return eligible
  }

  /**
   * Preserve workspaces until decision.
   */
  preserve(workspace_id: string): void {
    const ws = this.workspaces.get(workspace_id)
    if (ws && ws.state === 'abandoned') {
      ws.state = 'active' // Preserve
    }
  }
}
