/**
 * Worker Pool — 슬롯 기반 동시성 제어
 *
 * enqueue → 슬롯 배정 → 실행 → 완료 → 다음 큐 디스패치
 * 최대 maxConcurrent개까지 동시 실행, 나머지는 대기 큐에서 FIFO + 우선순위 정렬
 */

import type { WorkerInfo } from "./types"
import { getWorkerState, updateWorker, getQueuedWorkers, getWorkerStats } from "./store"
import * as Log from "@saeeol/core/util/log"

const log = Log.create({ service: "worker-pool" })

export class WorkerPool {
  private slots: Map<number, WorkerInfo | null> = new Map()
  private onDoneCallbacks: Array<(w: WorkerInfo) => void> = []

  constructor(maxConcurrent: number) {
    for (let i = 0; i < maxConcurrent; i++) {
      this.slots.set(i, null)
    }
  }

  /** 큐에서 다음 워커를 꺼내서 빈 슬롯에 배치 */
  dispatch(): void {
    const state = getWorkerState()
    if (!state.enabled) return

    const queued = getQueuedWorkers()
    if (queued.length === 0) return

    for (const [slotIdx, current] of this.slots) {
      if (current === null && queued.length > 0) {
        const next = queued.shift()!
        this.assignSlot(slotIdx, next)
      }
    }
  }

  private assignSlot(slotIndex: number, worker: WorkerInfo): void {
    updateWorker(worker.id, { status: "spawning" })
    this.slots.set(slotIndex, worker)
    log.info("assigned slot", { slot: slotIndex, worker: worker.id })
  }

  /** 워커가 실행 시작됨 */
  markRunning(workerID: string, sessionID: string): void {
    updateWorker(workerID, { status: "running", sessionID })
  }

  /** 워커 완료 → 슬롯 해제 → 다음 디스패치 */
  markDone(workerID: string, output?: string, error?: string): void {
    const state = getWorkerState()
    const worker = state.workers[workerID]
    if (!worker) return

    const status = error ? "error" : "done"
    updateWorker(workerID, { status, output, error, completed: Date.now() })

    // 슬롯 해제
    for (const [idx, w] of this.slots) {
      if (w?.id === workerID) {
        this.slots.set(idx, null)
        break
      }
    }

    // 콜백 실행 (최신 상태에서 읽기)
    const updated = getWorkerState().workers[workerID]
    if (updated) {
      for (const cb of this.onDoneCallbacks) cb(updated)
    }

    // 다음 대기 워커 디스패치
    this.dispatch()
  }

  /** 워커 취소 → 슬롯 해제 */
  markCancelled(workerID: string): void {
    updateWorker(workerID, { status: "cancelled", completed: Date.now() })

    for (const [idx, w] of this.slots) {
      if (w?.id === workerID) {
        this.slots.set(idx, null)
        break
      }
    }
    this.dispatch()
  }

  /** 전체 취소 */
  cancelAll(): void {
    const state = getWorkerState()
    for (const [idx, w] of this.slots) {
      if (w) {
        updateWorker(w.id, { status: "cancelled", completed: Date.now() })
        this.slots.set(idx, null)
      }
    }
  }

  /** 빈 슬롯 수 */
  availableSlots(): number {
    let count = 0
    for (const [, w] of this.slots) {
      if (w === null) count++
    }
    return count
  }

  /** 활성 슬롯 수 */
  activeSlots(): number {
    return this.slots.size - this.availableSlots()
  }

  /** 완료 콜백 등록 */
  onDone(cb: (w: WorkerInfo) => void): void {
    this.onDoneCallbacks.push(cb)
  }

  /** 토큰 업데이트 */
  updateTokens(workerID: string, input: number, output: number, reasoning: number, cost: number): void {
    updateWorker(workerID, { tokens: { input, output, reasoning }, cost })
  }

  /** 진행률 업데이트 */
  updateProgress(workerID: string, progress: number, output?: string): void {
    updateWorker(workerID, { progress, output })
  }
}

/** 글로벌 풀 인스턴스 */
let poolInstance: WorkerPool | null = null

export function getWorkerPool(): WorkerPool {
  if (!poolInstance) {
    const config = getWorkerState().config
    poolInstance = new WorkerPool(config.maxConcurrent)
  }
  return poolInstance
}

export function resetWorkerPool(): void {
  poolInstance = null
}
