/**
 * Cron execution engine.
 * Singleton scheduler that checks all enabled tasks every 60 seconds.
 */

import { cronTaskRepo } from '@/lib/database/repositories/cron-task.repo'
import { taskExecutionRepo } from '@/lib/database/repositories/task-execution.repo'
import crypto from 'crypto'
import { matchesCron } from './cron-parser'
import { executeTask } from './executor'

type CronTask = NonNullable<ReturnType<typeof cronTaskRepo.findById>>

declare const globalThis: {
  __forgeCronEngine?: CronEngine
} & typeof global

class CronEngine {
  private timer: ReturnType<typeof setInterval> | null = null
  private running = false
  private executingTasks = new Set<string>()

  start(): void {
    if (this.running) return
    this.running = true
    console.log('[CronEngine] Started')

    // Check immediately on start, then every 60 seconds
    this.tick()
    this.timer = setInterval(() => this.tick(), 60_000)
  }

  stop(): void {
    this.running = false
    if (this.timer) {
      clearInterval(this.timer)
      this.timer = null
    }
    console.log('[CronEngine] Stopped')
  }

  isRunning(): boolean {
    return this.running
  }

  private async tick(): Promise<void> {
    if (!this.running) return

    const tasks = cronTaskRepo.listEnabledCronTasks()
    const now = new Date()

    for (const task of tasks) {
      if (!task.schedule.trim()) continue
      if (this.executingTasks.has(task.id)) continue // skip if already executing

      if (matchesCron(task.schedule, now)) {
        this.executingTasks.add(task.id)
        this.runTask(task).finally(() => {
          this.executingTasks.delete(task.id)
        })
      }
    }
  }

  private async runTask(task: CronTask): Promise<void> {
    console.log(`[CronEngine] Executing task: ${task.name} (${task.id})`)

    const startTime = new Date().toISOString()

    try {
      const { status, result, sessionId } = await executeTask(task)

      // Record execution with session_id
      const execId = crypto.randomUUID()
      taskExecutionRepo.create({
        id: execId,
        taskId: task.id,
        taskName: task.name,
        result,
        status,
        sessionId: sessionId || '',
        executedAt: startTime,
      })

      // Update task last run
      cronTaskRepo.updateLastRun(task.id, result.slice(0, 200), startTime)

      // Auto-disable "once" tasks (specific day+month in cron = one-time task)
      const cronParts = task.schedule.split(/\s+/)
      if (cronParts.length === 5 && cronParts[2] !== '*' && cronParts[3] !== '*') {
        cronTaskRepo.setEnabled(task.id, 0)
        console.log(`[CronEngine] One-time task ${task.name} auto-disabled after execution`)
      }

      console.log(`[CronEngine] Task ${task.name}: ${status} — ${result.slice(0, 80)}`)
    } catch (err) {
      const errorMsg = err instanceof Error ? err.message : 'Unknown error'
      console.error(`[CronEngine] Task ${task.name} failed:`, errorMsg)

      const execId = crypto.randomUUID()
      taskExecutionRepo.create({
        id: execId,
        taskId: task.id,
        taskName: task.name,
        result: errorMsg,
        status: 'error',
        sessionId: '',
        executedAt: startTime,
      })

      cronTaskRepo.updateLastRun(task.id, `Error: ${errorMsg.slice(0, 180)}`, startTime)
    }
  }
}

export function getCronEngine(): CronEngine {
  if (!globalThis.__forgeCronEngine) {
    globalThis.__forgeCronEngine = new CronEngine()
  }
  return globalThis.__forgeCronEngine
}
