/**
 * Proposal expiry cron — proposal-expiry-deadline (wave-12).
 *
 * POST /api/cron/proposal-expiry
 *
 * Guarded by CRON_SECRET (timing-safe comparison).
 * Schedule: `0 6 * * *` (daily at 06:00 UTC).
 *
 * Algorithm:
 *   1. Bulk-expire all SENT/VIEWED proposals where expires_at < now
 *   2. For each expired proposal:
 *      a. Resolve OWNER/ADMIN members for tenant
 *      b. Insert proposal_expired notification per member (in transaction with lead step)
 *      c. If lead_id set and lead.stage = PROPOSAL and no other live proposal → revert to QUALIFIED
 *   3. Per-proposal errors are caught so one failure does not abort the run.
 */
import { Hono } from 'hono'
import { timingSafeEqual } from '@zync/auth'
import { createDb } from '@zync/db/queries'
import {
  expireOverdueProposals,
  getOwnerAdminUserIds,
  insertProposalExpiredNotification,
  revertLeadToQualifiedIfNoLiveProposal,
} from '@zync/db/queries'
import type { AppEnv } from '../../types'

export const proposalExpiryCron = new Hono<AppEnv>()

proposalExpiryCron.post('/', async (c) => {
  // ── 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)

  // 1. Bulk-expire all overdue proposals
  const expired = await expireOverdueProposals(db)

  let notificationsInserted = 0
  let leadsReverted = 0
  let errors = 0

  // 2. Process each expired proposal
  for (const row of expired) {
    try {
      // a. Resolve recipients
      const userIds = await getOwnerAdminUserIds(db, row.tenantId)

      // b. Insert notifications + lead-stage write in a transaction
      await db.transaction(async (tx) => {
        for (const userId of userIds) {
          await insertProposalExpiredNotification(tx, row.tenantId, userId, row.id, row.title ?? '')
          notificationsInserted++
        }

        // c. Lead state-exit
        if (row.leadId) {
          const reverted = await revertLeadToQualifiedIfNoLiveProposal(
            tx,
            row.tenantId,
            row.leadId,
            row.id,
          )
          if (reverted) leadsReverted++
        }
      })
    } catch (err) {
      errors++
      console.error(`[proposalExpiryCron] error for proposal ${row.id}:`, err)
    }
  }

  console.log(
    `[proposalExpiryCron] expired=${expired.length} notifications=${notificationsInserted} leads_reverted=${leadsReverted} errors=${errors}`,
  )
  return c.json({ expired: expired.length, notificationsInserted, leadsReverted, errors }, 200)
})
