import RelanceTask from '#models/relance_task'
import Invoice from '#models/invoice'
import User from '#models/user'
import { sendRelanceEmail } from '#services/mail_dispatcher'
import { recordActivity } from '#services/activity_recorder'
import * as clock from '#services/clock'
import db from '@adonisjs/lucid/services/db'
import logger from '@adonisjs/core/services/logger'

/**
 * Worker BullMQ pour la queue `relances`. Idempotent : si la task n'est
 * plus `scheduled` (déjà envoyée, annulée, ou échouée définitivement),
 * no-op.
 *
 * Cas critiques :
 *  - Invoice payée/annulée entre temps → cancel la task (pas d'envoi)
 *  - Step `requires_manual_validation` (mise en demeure) → on n'envoie
 *    PAS, on log un activity_event 'warning_drafted' que l'utilisateur
 *    devra valider manuellement (cf. CLAUDE.md → Principes produit).
 *  - Sinon : envoi de l'email + bump rubis (1 rubis = 10 min libérées).
 */
export async function sendRelanceJob(jobData: { taskId: string }) {
  logger.info({ taskId: jobData.taskId }, 'sendRelanceJob: pick-up')

  const task = await RelanceTask.query()
    .where('id', jobData.taskId)
    .preload('planStep')
    .first()
  if (!task) {
    logger.warn({ taskId: jobData.taskId }, 'sendRelanceJob: task not found, skipping')
    return
  }
  if (task.status !== 'scheduled') {
    logger.info(
      { taskId: task.id, status: task.status },
      'sendRelanceJob: task not scheduled (already sent / cancelled), skipping'
    )
    return
  }

  const invoice = await Invoice.query()
    .where('id', task.invoiceId)
    .preload('client')
    .preload('organization')
    .first()
  if (!invoice) {
    logger.warn(
      { taskId: task.id, invoiceId: task.invoiceId },
      'sendRelanceJob: invoice not found, cancelling task'
    )
    task.status = 'cancelled'
    await task.save()
    return
  }

  // Hook critique : la facture peut avoir été payée entre la programmation
  // et l'exécution. On vérifie avant d'envoyer.
  if (invoice.status === 'paid' || invoice.status === 'cancelled') {
    logger.info(
      { taskId: task.id, invoiceId: invoice.id, status: invoice.status },
      'sendRelanceJob: invoice paid/cancelled between schedule and execution, cancelling task'
    )
    task.status = 'cancelled'
    await task.save()
    return
  }

  const step = task.planStep
  const user = await User.query().where('organization_id', invoice.organizationId).first()

  // Mise en demeure : on génère un brouillon, on n'envoie pas (cf. CLAUDE.md).
  if (step.requiresManualValidation) {
    await db.transaction(async (trx) => {
      task.useTransaction(trx)
      task.status = 'sent' // On considère la task "traitée" — le brouillon est l'output
      task.sentAt = await clock.now(invoice.organizationId)
      await task.save()

      await recordActivity({
        organizationId: invoice.organizationId,
        kind: 'warning_drafted',
        label: `Brouillon mise en demeure prêt — <b>${invoice.client.name}</b> (${invoice.numero})`,
        meta: {
          invoiceId: invoice.id,
          clientId: invoice.clientId,
          planStepOrder: step.order,
        },
        trx,
      })
    })
    return
  }

  // Envoi normal
  logger.info(
    {
      taskId: task.id,
      invoiceId: invoice.id,
      numero: invoice.numero,
      to: invoice.client.email,
      stepOrder: step.order,
      offsetDays: step.offsetDays,
    },
    'sendRelanceJob: envoi mail relance'
  )
  await sendRelanceEmail({
    invoice,
    client: invoice.client,
    step,
    user,
    organization: invoice.organization,
  })
  logger.info(
    { taskId: task.id, invoiceId: invoice.id, numero: invoice.numero },
    'sendRelanceJob: mail relance envoyé OK'
  )

  const sentAt = await clock.now(invoice.organizationId)
  await db.transaction(async (trx) => {
    task.useTransaction(trx)
    task.status = 'sent'
    task.sentAt = sentAt
    await task.save()

    invoice.useTransaction(trx)
    // Première relance envoyée → status passe en `in_relance` (la facture
    // sort de l'état "pending" silencieux).
    if (invoice.status === 'pending') {
      invoice.status = 'in_relance'
    }
    invoice.rubisEarned = invoice.rubisEarned + 1
    await invoice.save()

    await trx
      .from('organizations')
      .where('id', invoice.organizationId)
      .increment('rubis_count', 1)

    await recordActivity({
      organizationId: invoice.organizationId,
      kind: 'relance_sent',
      label: `Relance J${step.offsetDays >= 0 ? '+' : ''}${step.offsetDays} envoyée à <b>${invoice.client.name}</b>`,
      meta: {
        invoiceId: invoice.id,
        clientId: invoice.clientId,
        planStepOrder: step.order,
      },
      trx,
    })
  })
}
