// Daily-ish scheduler for the audit archive run. Disabled by default
// (ARCHIVE_ENABLED=false) — production turns it on once volumes warrant.
// The operator UI can force a run via POST /audit/archive/run regardless
// of this flag.

import {
  Injectable,
  Logger,
  type OnApplicationBootstrap,
  type OnModuleDestroy,
} from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { ArchiveService } from './archive.service.js'

// One hour. Inside each tick we check if the current UTC hour matches the
// configured run-hour; only one of 24 ticks per day actually invokes the
// archive. Cheap and simple — no real cron lib needed.
const TICK_MS = 60 * 60 * 1000
const DEFAULT_RUN_HOUR_UTC = 3 // 03:00 UTC daily

@Injectable()
export class ArchiveWorker implements OnApplicationBootstrap, OnModuleDestroy {
  private readonly logger = new Logger(ArchiveWorker.name)
  private readonly enabled: boolean
  private readonly runHour: number
  private timer: NodeJS.Timeout | null = null
  private lastRunDay: string | null = null

  constructor(
    private readonly archive: ArchiveService,
    config: ConfigService,
  ) {
    this.enabled = config.get('ARCHIVE_ENABLED') === 'true'
    this.runHour = Number(config.get('ARCHIVE_RUN_HOUR_UTC') ?? DEFAULT_RUN_HOUR_UTC)
  }

  onApplicationBootstrap(): void {
    if (!this.enabled) {
      this.logger.log(
        'ARCHIVE_ENABLED=false — archive scheduler dormant. Operator UI can force runs.',
      )
      return
    }
    this.logger.log(`Archive scheduler active · runs daily at ${this.runHour}:00 UTC`)
    // Fire once on startup in case we missed a window; the day-guard prevents
    // double-runs within the same UTC day.
    void this.tick()
    this.timer = setInterval(() => void this.tick(), TICK_MS)
  }

  onModuleDestroy(): void {
    if (this.timer) clearInterval(this.timer)
  }

  private async tick(): Promise<void> {
    const now = new Date()
    if (now.getUTCHours() !== this.runHour) return
    const today = now.toISOString().slice(0, 10) // YYYY-MM-DD UTC
    if (this.lastRunDay === today) return // already ran today
    this.lastRunDay = today

    this.logger.log(`Archive tick fired for ${today}`)
    try {
      const res = await this.archive.runOnce()
      if (res.ok && res.eventCount) {
        this.logger.log(`Archive complete: ${res.eventCount} events (${res.startSeq}-${res.endSeq})`)
      } else if (res.ok) {
        this.logger.log(`Archive complete: ${res.reason ?? 'no-op'}`)
      } else {
        this.logger.error(`Archive failed: ${res.reason}`)
      }
    } catch (err) {
      this.logger.error(
        `Archive tick crashed: ${err instanceof Error ? err.message : String(err)}`,
      )
    }
  }
}
