import mail from '@adonisjs/mail/services/main'
import logger from '@adonisjs/core/services/logger'
import env from '#start/env'
import { DateTime } from 'luxon'
import { render } from '@react-email/components'
import { renderTemplate, formatAmountFr, formatDateFr } from '#services/template'
import * as clock from '#services/clock'
import { captureEmailIfDemo } from '#services/demo/capture'
import type Invoice from '#models/invoice'
import type Client from '#models/client'
import type PlanStep from '#models/plan_step'
import type User from '#models/user'
import type Organization from '#models/organization'

import { CheckinEmail } from '#mails/checkin_email'
import { RelanceEmail } from '#mails/relance_email'

type RelancePayload = {
  invoice: Invoice
  client: Client
  step: PlanStep
  user: User | null
  organization?: Organization | null
}

/**
 * Construit l'objet `vars` interpolé dans subject/body. Exposé pour
 * permettre la preview côté contrôleur (wizard de création de plan)
 * avec les mêmes variables que ce qui sera réellement envoyé.
 *
 * Variables disponibles :
 *  - {{client.name}}, {{client.email}}
 *  - {{client.contactFirstName}}, {{client.contactLastName}} (peuvent être vides)
 *  - {{numero}}, {{amount}}, {{dueDate}}, {{issueDate}}
 *  - {{daysLate}}  (jours de retard depuis dueDate, négatif = avant échéance)
 *  - {{user.fullName}}, {{user.companyName}}
 *  - {{signature}}
 */
export function buildRelanceVars({
  invoice,
  client,
  user,
  organization,
  now = DateTime.utc(),
}: {
  invoice: Pick<Invoice, 'numero' | 'amountTtcCents' | 'dueDate' | 'issueDate'>
  client: Pick<Client, 'name' | 'email' | 'contactFirstName' | 'contactLastName'>
  user: Pick<User, 'fullName' | 'signature' | 'email'> | null
  organization?: Pick<Organization, 'name'> | null
  /** `now` injecté pour respecter virtualNow en mode démo. */
  now?: DateTime
}) {
  const dueDate = invoice.dueDate.toJSDate()
  // Jours de retard arrondis à l'entier — démo-aware via `now` injecté.
  const daysLate = Math.floor(
    now.startOf('day').diff(invoice.dueDate.startOf('day'), 'days').days
  )
  return {
    client: {
      name: client.name,
      email: client.email,
      contactFirstName: client.contactFirstName ?? '',
      contactLastName: client.contactLastName ?? '',
    },
    user: {
      fullName: user?.fullName ?? '',
      companyName: organization?.name ?? '',
    },
    numero: invoice.numero,
    amount: formatAmountFr(invoice.amountTtcCents),
    dueDate: formatDateFr(dueDate),
    issueDate: formatDateFr(invoice.issueDate.toJSDate()),
    daysLate: String(daysLate),
    signature: user?.signature ?? user?.fullName ?? '',
  }
}

/**
 * Envoie un email de relance à un client à partir d'un step.
 * Le subject/body du step contiennent des placeholders Mustache-like
 * qu'on interpole avant l'envoi (cf. `buildRelanceVars`).
 *
 * Le mailer effectif est piloté par MAIL_DRIVER (`smtp` Mailpit en dev,
 * `resend` en prod).
 */
export async function sendRelanceEmail({
  invoice,
  client,
  step,
  user,
  organization,
}: RelancePayload) {
  const vars = buildRelanceVars({
    invoice,
    client,
    user,
    organization,
    now: await clock.now(invoice.organizationId),
  })

  const subject = renderTemplate(step.subject, vars)
  const body = renderTemplate(step.body, vars)
  const fromAddress = env.get('MAIL_FROM_ADDRESS', 'relances@rubis.pro')
  // Le client final connaît l'org (ex: "Arthur Barré"), pas Rubis. On utilise
  // le nom de l'org comme display name visible côté client. Fallback :
  // user.fullName, puis MAIL_FROM_NAME (= "Rubis sur l'ongle") en dernier
  // recours si l'org n'a pas de nom posé.
  // L'adresse technique reste sur notre domaine vérifié (SPF/DKIM Resend).
  const fromName =
    organization?.name?.trim() ||
    user?.fullName?.trim() ||
    env.get('MAIL_FROM_NAME', "Rubis sur l'ongle")

  // Calcule daysLate pour le récap visuel dans le HTML.
  const nowOrg = await clock.now(invoice.organizationId)
  const daysLate = Math.floor(
    nowOrg.startOf('day').diff(invoice.dueDate.startOf('day'), 'days').days
  )

  const landingUrl = env.get('LANDING_URL', 'https://rubis.pro')

  // Rendu HTML via React Email — DA Rubis (header rubis-deep + card cream).
  const htmlBody = await render(
    RelanceEmail({
      brandName: fromName,
      invoice: {
        numero: invoice.numero,
        amountFormatted: formatAmountFr(invoice.amountTtcCents),
        dueDateFormatted: formatDateFr(invoice.dueDate.toJSDate()),
        daysLate,
      },
      bodyText: body,
      landingUrl,
    })
  )

  // FORK DÉMO — unique point où l'app dévie de la prod. Si l'org est
  // en mode démo, on capture l'email dans demo_captured_emails au lieu
  // de l'envoyer via Resend. Tout le reste du pipeline (idempotence,
  // status update, rubis bump) tourne identique.
  const captured = await captureEmailIfDemo({
    organizationId: invoice.organizationId,
    kind: 'relance',
    to: { email: client.email, name: client.name },
    from: { email: fromAddress, name: fromName },
    replyTo: user?.email ?? null,
    subject,
    body,
    meta: { invoiceId: invoice.id, clientId: client.id, stepOrder: step.order },
  })
  if (captured) {
    logger.info(
      { invoiceId: invoice.id, numero: invoice.numero, to: client.email },
      'sendRelanceEmail: capturé en mode démo (pas d\'envoi réel)'
    )
    return // demo : ne pas envoyer pour de vrai
  }

  const driver = env.get('MAIL_DRIVER', 'smtp')
  logger.info(
    {
      invoiceId: invoice.id,
      numero: invoice.numero,
      to: client.email,
      from: fromAddress,
      driver,
      subjectPreview: subject.slice(0, 80),
    },
    'sendRelanceEmail: envoi via driver'
  )
  try {
    const mailer = mail.use(driver)
    await mailer.send((m) => {
      m.from(fromAddress, fromName)
        .to(client.email, client.name)
        .subject(subject)
        // HTML rendu depuis le composant React Email (DA Rubis).
        .html(htmlBody)
        // Plain text fallback : améliore la délivrabilité (anti-spam) et
        // sert pour les clients qui désactivent le HTML.
        .text(body)
      // Reply-To pointe sur l'utilisateur Rubis : si le client final répond
      // à la relance, sa réponse arrive chez le patron de la TPE, pas dans
      // notre boîte transactionnelle.
      if (user?.email) {
        m.replyTo(user.email, user.fullName ?? user.email)
      }
    })
    logger.info(
      { invoiceId: invoice.id, numero: invoice.numero, driver },
      'sendRelanceEmail: send OK'
    )
  } catch (err) {
    logger.error(
      { err, invoiceId: invoice.id, numero: invoice.numero, driver },
      'sendRelanceEmail: échec envoi'
    )
    throw err
  }
}

type CheckinPayload = {
  invoice: Invoice
  client: Client
  user: User
  paidUrl: string
  pendingUrl: string
}

/**
 * Envoie le check-in à l'**utilisateur** (pas au client). Lui demande
 * si la facture a été payée, avec 2 liens publics qui modifient l'état
 * côté API et redirigent ensuite vers le SPA.
 *
 * Texte brut V1. Un template HTML viendra quand on aura figé le look
 * graphique (cf. ADR-021).
 */
export async function sendCheckinEmail({
  invoice,
  client,
  user,
  paidUrl,
  pendingUrl,
}: CheckinPayload) {
  const subject = `Facture ${invoice.numero} — payée par ${client.name} ?`
  const body = `Bonjour ${user.fullName ?? ''},

La facture ${invoice.numero} (${formatAmountFr(invoice.amountTtcCents)}) émise pour ${client.name}
arrive à échéance aujourd'hui (${formatDateFr(invoice.dueDate.toJSDate())}).

Avant que Rubis n'envoie la première relance, dites-nous où vous en êtes :

✓  J'ai été payé(e), pas besoin de relancer :
   ${paidUrl}

→  Toujours en attente, lance la relance comme prévu :
   ${pendingUrl}

Ces liens expirent dans 24h.

Merci,
L'équipe Rubis`

  const fromAddress = env.get('MAIL_FROM_ADDRESS', 'relances@rubis.pro')
  // Le check-in vient FROM Rubis (notification interne à l'user, pas au
  // client final). On garde donc le brand "Rubis sur l'ongle" comme display,
  // PAS le nom de l'org.
  const fromName = env.get('MAIL_FROM_NAME', "Rubis sur l'ongle")

  // Rendu HTML — DA Rubis avec 2 boutons CTA Oui/Non.
  const landingUrl = env.get('LANDING_URL', 'https://rubis.pro')
  const htmlBody = await render(
    CheckinEmail({
      invoice: {
        numero: invoice.numero,
        amountFormatted: formatAmountFr(invoice.amountTtcCents),
        dueDateFormatted: formatDateFr(invoice.dueDate.toJSDate()),
      },
      client: { name: client.name },
      user: { fullName: user.fullName ?? null },
      paidUrl,
      pendingUrl,
      landingUrl,
    })
  )

  // FORK DÉMO — capture si demoMode (cf. sendRelanceEmail).
  const captured = await captureEmailIfDemo({
    organizationId: invoice.organizationId,
    kind: 'checkin',
    to: { email: user.email, name: user.fullName ?? user.email },
    from: { email: fromAddress, name: fromName },
    replyTo: null,
    subject,
    body,
    meta: { invoiceId: invoice.id, clientId: client.id, paidUrl, pendingUrl },
  })
  if (captured) return

  const mailer = mail.use(env.get('MAIL_DRIVER', 'smtp'))
  await mailer.send((m) => {
    m.from(fromAddress, fromName)
      .to(user.email, user.fullName ?? user.email)
      .subject(subject)
      // HTML rendu via React Email (DA Rubis), texte brut en fallback.
      .html(htmlBody)
      .text(body)
  })
}
