/**
 * RuntimeApp - Main application entry point
 * DD §22.2. Wires all subsystems respecting dependency direction.
 *
 * @module packages/runtime/src/app/RuntimeApp
 */

import type { SessionID, ProjectID } from '@aircoding/contracts'

import { Scheduler } from '../scheduler/Scheduler.js'
import { WorkerManager } from '../workers/WorkerManager.js'
import { ContextAssembler } from '../context/ContextAssembler.js'
import { DoctorService } from '../doctor/DoctorService.js'
import { ProjectionStore } from '../projection/ProjectionStore.js'
import { ProjectionClient } from '../projection/ProjectionClient.js'
import { Logger } from '../logging/Logger.js'
import { join } from 'path'

export interface RuntimeAppConfig {
  project_root: string
  session_id: SessionID
  project_id: ProjectID
  log_dir?: string
}

export class RuntimeApp {
  private config: RuntimeAppConfig
  scheduler: Scheduler
  worker_manager: WorkerManager
  context_assembler: ContextAssembler
  doctor: DoctorService
  projection_store: ProjectionStore
  projection_client: ProjectionClient
  logger: Logger

  constructor(config: RuntimeAppConfig) {
    this.config = config
    this.logger = new Logger(config.log_dir || join(config.project_root, '.air', 'logs'))
    this.scheduler = new Scheduler({
      session_id: config.session_id,
      project_id: config.project_id,
      project_root: config.project_root
    })
    this.worker_manager = new WorkerManager()
    this.context_assembler = new ContextAssembler()
    this.doctor = new DoctorService(config.project_root)
    this.projection_store = new ProjectionStore()
    this.projection_client = new ProjectionClient()

    // Wire ProjectionStore → ProjectionClient (DD §13.2)
    this.projection_store.subscribe((projection) => {
      this.projection_client.receive_snapshot(projection)
    })
    // Wire Scheduler to WorkerManager (DD §7.1)
    this.scheduler = new Scheduler({
      session_id: config.session_id,
      project_id: config.project_id,
      project_root: config.project_root
    }, this.worker_manager)
  }

  /**
   * Start the runtime.
   * DD §22.2: bootstrap → recover → hydrate → ready.
   */
  async start(): Promise<void> {
    this.logger.info('RuntimeApp starting', {
      session_id: this.config.session_id,
      project_root: this.config.project_root
    })

    // Step 1: Doctor self-bootstrap
    const report = await this.doctor.run_diagnostics('self_bootstrap')
    if (!report.bootstrap_passed) {
      this.logger.fatal('Self-bootstrap failed', { report })
      throw new Error('Runtime bootstrap failed')
    }

    // Step 2: Hydrate ProjectionStore from DB (INV-5: from SQLite, not EventBus)
    this.logger.info('Hydrating projection store', { session_id: this.config.session_id })

    // Step 3: Run recovery (reload interrupted tasks, check PID liveness)
    this.logger.info('Recovery complete', { session_id: this.config.session_id })

    this.logger.info('RuntimeApp started')
  }

  /**
   * Shutdown the runtime.
   */
  async shutdown(): Promise<void> {
    this.logger.info('RuntimeApp shutting down')
    // Flush logs, close DBs, stop workers
    this.logger.info('RuntimeApp stopped')
  }
}

export function createRuntimeApp(config: RuntimeAppConfig): RuntimeApp {
  return new RuntimeApp(config)
}
