import { and, asc, eq, lte } from "drizzle-orm"
import { db } from "../../db/index.js"
import { alertOutbox } from "../../db/schema.js"
import { appendAlertHistory, sendTelegramAlertMessage } from "../alerts-service.js"

export type AlertOutboxPayload = {
  text: string
  chatId?: string
  history: {
    id: string
    ruleId?: string | null
    groupId?: string | null
    ruleName: string
    severity: "critical" | "warning" | "info"
    message: string
    firedAt: string
  }
}

function safeParsePayload(raw: string): AlertOutboxPayload | null {
  try {
    return JSON.parse(raw) as AlertOutboxPayload
  } catch {
    return null
  }
}

export function enqueueTelegramOutbox(input: {
  id: string
  dedupeKey: string
  payload: AlertOutboxPayload
  maxRetries?: number
}): boolean {
  const existing = db
    .select()
    .from(alertOutbox)
    .where(eq(alertOutbox.dedupeKey, input.dedupeKey))
    .limit(1)
    .all()[0]
  if (existing && (existing.status === "pending" || existing.status === "sent")) return false
  const nowIso = new Date().toISOString()
  db.insert(alertOutbox)
    .values({
      id: input.id,
      dedupeKey: input.dedupeKey,
      channel: "telegram",
      status: "pending",
      retryCount: 0,
      maxRetries: Math.max(1, Math.floor(input.maxRetries ?? 3)),
      nextAttemptAt: nowIso,
      payloadJson: JSON.stringify(input.payload),
    })
    .run()
  return true
}

export function dispatchPendingOutbox(limit = 25): Promise<{ sent: number; failed: number; errors: string[] }> {
  return (async () => {
    const nowIso = new Date().toISOString()
    const rows = db
      .select()
      .from(alertOutbox)
      .where(and(eq(alertOutbox.status, "pending"), lte(alertOutbox.nextAttemptAt, nowIso)))
      .orderBy(asc(alertOutbox.createdAt))
      .limit(Math.max(1, limit))
      .all()

    let sent = 0
    let failed = 0
    const errors: string[] = []

    for (const row of rows) {
      const payload = safeParsePayload(row.payloadJson)
      if (!payload) {
        db.update(alertOutbox)
          .set({
            status: "failed",
            lastError: "invalid payload_json",
          })
          .where(eq(alertOutbox.id, row.id))
          .run()
        failed += 1
        continue
      }
      const send = await sendTelegramAlertMessage({ text: payload.text, chatId: payload.chatId })
      if (send.ok) {
        db.update(alertOutbox)
          .set({
            status: "sent",
            sentAt: new Date().toISOString(),
            lastError: null,
          })
          .where(eq(alertOutbox.id, row.id))
          .run()
        appendAlertHistory({
          id: payload.history.id,
          ruleId: payload.history.ruleId ?? null,
          groupId: payload.history.groupId ?? null,
          ruleName: payload.history.ruleName,
          severity: payload.history.severity,
          message: payload.history.message,
          sentOk: true,
          firedAt: payload.history.firedAt,
        })
        sent += 1
        continue
      }
      const nextRetry = row.retryCount + 1
      const exhausted = nextRetry >= row.maxRetries
      db.update(alertOutbox)
        .set({
          retryCount: nextRetry,
          status: exhausted ? "failed" : "pending",
          nextAttemptAt: new Date(Date.now() + Math.min(300_000, nextRetry * 15_000)).toISOString(),
          lastError: send.error,
        })
        .where(eq(alertOutbox.id, row.id))
        .run()
      if (exhausted) {
        appendAlertHistory({
          id: payload.history.id,
          ruleId: payload.history.ruleId ?? null,
          groupId: payload.history.groupId ?? null,
          ruleName: payload.history.ruleName,
          severity: payload.history.severity,
          message: payload.history.message,
          sentOk: false,
          firedAt: payload.history.firedAt,
        })
        failed += 1
      } else {
        errors.push(`outbox ${row.id}: ${send.error}`)
      }
    }

    return { sent, failed, errors }
  })()
}
