/**
 * Daily recurring task generator cron — recurring-tasks-templates.
 *
 * POST /api/cron/recurring-task-generator
 *
 * Guarded by CRON_SECRET (timing-safe comparison). Runs at 01:00 UTC daily.
 *
 * For each active recurring_tasks row:
 *   - Expand RRULE over [now, now + advance_days] window
 *   - For each occurrence: call generateTaskFromRecurring (ON CONFLICT DO NOTHING
 *     backed by idx_tasks_recurring_due makes re-runs idempotent)
 *   - After all occurrences: touchRecurringGenerated to set last_generated_at
 *
 * Labels are expanded to task_labels rows inside generateTaskFromRecurring.
 * Per-row errors are caught so one bad RRULE does not abort the entire run.
 */
import { Hono } from 'hono'
import { RRule } from 'rrule'
import { timingSafeEqual } from '@zync/auth'
import {
  createDb,
  listActiveRecurringTasks,
  generateTaskFromRecurring,
  touchRecurringGenerated,
} from '@zync/db/queries'
import type { AppEnv } from '../../types'

export const recurringTaskGeneratorRoute = new Hono<AppEnv>()

recurringTaskGeneratorRoute.post('/', async (c) => {
  // Timing-safe CRON_SECRET guard
  const secret = c.req.header('x-cron-secret') ?? ''
  const expected = c.env.CRON_SECRET
  if (!expected || expected.length < 16) {
    return c.json({ error: 'Server misconfigured' }, 500)
  }

  if (!timingSafeEqual(secret, expected)) {
    return c.json({ error: 'Unauthorized' }, 401)
  }

  const db = createDb(c.env)
  const now = new Date()

  const activeRows = await listActiveRecurringTasks(db)

  let generated = 0
  let skipped = 0
  let errors = 0

  for (const recurring of activeRows) {
    try {
      const rule = RRule.fromString(recurring.recurrence)
      const windowEnd = new Date(now.getTime() + recurring.advanceDays * 86_400_000)

      // inclusive = true to include boundary dates
      const occurrences = rule.between(now, windowEnd, true)

      for (const occurrence of occurrences) {
        // Cast to YYYY-MM-DD date string (due_date is a DATE column)
        const dueDate = occurrence.toISOString().slice(0, 10)

        try {
          const { taskId } = await generateTaskFromRecurring(
            db,
            recurring.tenantId,
            recurring,
            dueDate,
          )
          if (taskId) {
            generated++
          } else {
            skipped++ // ON CONFLICT DO NOTHING — duplicate slot
          }
        } catch (occErr) {
          errors++
          console.error(
            `[recurringTaskGenerator] generateTaskFromRecurring error for recurringId=${recurring.id} dueDate=${dueDate}: ${occErr}`,
          )
        }
      }

      // Update last_generated_at after processing this row's occurrences
      await touchRecurringGenerated(db, recurring.tenantId, recurring.id)
    } catch (rowErr) {
      errors++
      console.error(
        `[recurringTaskGenerator] row error for recurringId=${recurring.id}: ${rowErr}`,
      )
    }
  }

  return c.json({
    ok: true,
    processed: activeRows.length,
    generated,
    skipped,
    errors,
  })
})
