<template>
  <q-page padding>
    <div class="row items-center q-mb-md">
      <q-btn flat dense round icon="arrow_back" :to="'/campaigns'" class="q-mr-sm" />
      <div class="text-h5">{{ campaign?.name || id }}</div>
      <q-chip dense class="q-ml-md" :color="statusColor(campaign?.status)" text-color="white" :label="statusLabel(campaign?.status)" />
      <q-space />
      <q-btn
        v-if="campaign?.recipients?.length"
        flat dense icon="file_download" label="CSV" class="q-mr-sm"
        :href="reportCsvUrl" download
      >
        <q-tooltip>Télécharger le rapport (shortlinks Giftbit, emails, statuts d'envoi)</q-tooltip>
      </q-btn>
      <q-btn v-if="canCreateReminder" flat dense icon="schedule" color="orange-9"
             label="Créer une relance" class="q-mr-sm"
             :loading="creatingReminder" @click="confirmCreateReminder">
        <q-tooltip>Cloner cette campagne pour les destinataires qui n'ont PAS cliqué le cadeau encore</q-tooltip>
      </q-btn>
      <!-- Draft-only edit affordances: params dialog + jump to template
           editor. Both invisible once status flips off draft to keep
           operators from accidentally mutating a campaign mid-send. -->
      <q-btn v-if="campaign?.status === 'draft'" flat dense icon="palette" color="primary"
             label="Éditer le template" class="q-mr-sm"
             :href="templateEditorHref" target="_blank">
        <q-tooltip>Modifier le contenu HTML du template dans un nouvel onglet (Unlayer)</q-tooltip>
      </q-btn>
      <q-btn v-if="campaign?.status === 'draft'" flat dense icon="tune" color="primary"
             label="Éditer les paramètres" class="q-mr-sm"
             @click="openEditParams">
        <q-tooltip>Sujet, expéditeur, montant affiché, choix de template (FR/EN), etc.</q-tooltip>
      </q-btn>
      <q-btn v-if="campaign?.status === 'draft'" unelevated color="primary" icon="send" label="Lancer l'envoi"
             :loading="resending" @click="relaunch" />
    </div>

    <!-- Counters bar -->
    <div class="row q-col-gutter-sm q-mb-md" v-if="campaign">
      <q-card flat bordered class="col-6 col-md-2"><q-card-section class="text-center">
        <div class="text-h5">{{ campaign.counters?.total || campaign.recipients?.length || 0 }}</div>
        <div class="text-caption text-grey-7">Total</div>
      </q-card-section></q-card>
      <q-card flat bordered class="col-6 col-md-2"><q-card-section class="text-center">
        <div class="text-h5 text-positive">{{ counterFor('sent') + counterFor('opened') + counterFor('clicked') }}</div>
        <div class="text-caption text-grey-7">Envoyés</div>
      </q-card-section></q-card>
      <q-card flat bordered class="col-6 col-md-2"><q-card-section class="text-center">
        <div class="text-h5 text-blue">{{ counterFor('clicked') }}</div>
        <div class="text-caption text-grey-7">
          Cliqués
          <q-icon name="info" size="12px" class="q-ml-xs">
            <q-tooltip>Tous liens confondus (CTA, support, footer…)</q-tooltip>
          </q-icon>
        </div>
      </q-card-section></q-card>
      <q-card flat bordered class="col-6 col-md-2"><q-card-section class="text-center">
        <div class="text-h5 text-deep-purple-7">🎁 {{ campaign.counters?.gift_clicked || 0 }}</div>
        <div class="text-caption text-grey-7">
          Cadeau cliqué
          <q-icon name="info" size="12px" class="q-ml-xs">
            <q-tooltip>Le destinataire a cliqué le bouton CTA / lien Giftbit — signal d'engagement réel avec l'offre</q-tooltip>
          </q-icon>
        </div>
      </q-card-section></q-card>
      <q-card flat bordered class="col-6 col-md-2"><q-card-section class="text-center">
        <div class="text-h5 text-orange">{{ counterFor('queued') }}</div>
        <div class="text-caption text-grey-7">En attente</div>
      </q-card-section></q-card>
      <q-card flat bordered class="col-6 col-md-2"><q-card-section class="text-center">
        <div class="text-h5 text-negative">{{ counterFor('failed') + counterFor('bounced') }}</div>
        <div class="text-caption text-grey-7">Échecs</div>
      </q-card-section></q-card>
      <q-card flat bordered class="col-6 col-md-2"><q-card-section class="text-center">
        <div class="text-h5 text-grey-7">{{ counterFor('pending') }}</div>
        <div class="text-caption text-grey-7">Non envoyés</div>
      </q-card-section></q-card>
    </div>

    <q-linear-progress
      v-if="campaign && (campaign.status === 'sending' || campaign.status === 'completed')"
      :value="sentRatio" :color="campaign.counters?.failed ? 'orange' : 'positive'" size="8px" class="q-mb-md"
    />

    <q-table
      v-if="campaign"
      :rows="campaign.recipients || []"
      :columns="columns" row-key="email"
      flat bordered dense
      :pagination="{ rowsPerPage: 50 }"
      :rows-per-page-options="[25, 50, 100, 0]"
    >
      <template v-slot:body-cell-status="props">
        <q-td :props="props">
          <q-chip dense size="sm" :color="statusColor(props.row.status)" text-color="white" :label="statusLabel(props.row.status)" />
          <q-icon v-if="props.row.gift_link_clicked" name="redeem" color="deep-purple-7" size="18px" class="q-ml-xs">
            <q-tooltip>Cadeau cliqué {{ props.row.gift_clicked_at ? `le ${new Date(props.row.gift_clicked_at).toLocaleString('fr-CA')}` : '' }}</q-tooltip>
          </q-icon>
        </q-td>
      </template>
      <template v-slot:body-cell-customer="props">
        <q-td :props="props">
          <span v-if="props.row.customer_id">
            <q-icon name="person" size="14px" class="q-mr-xs" />
            <a :href="`/#/clients/${props.row.customer_id}`" target="_blank" style="color:var(--q-primary)">
              {{ props.row.customer_name || props.row.customer_id }}
            </a>
            <q-chip dense size="xs" outline class="q-ml-xs">{{ props.row.match_method }}</q-chip>
          </span>
          <span v-else class="text-grey-6">—</span>
        </q-td>
      </template>
      <template v-slot:body-cell-gift_url="props">
        <q-td :props="props">
          <a :href="props.row.gift_url" target="_blank" class="text-grey-7" style="font-family:monospace; font-size:0.78rem">
            {{ shortLink(props.row.gift_url) }}
          </a>
          <q-btn v-if="giftbitAdminUrl(props.row.gift_url)"
                 flat dense round size="xs" icon="open_in_new" color="primary"
                 :href="giftbitAdminUrl(props.row.gift_url)" target="_blank" class="q-ml-xs">
            <q-tooltip>Voir sur Giftbit admin (vérifier statut de redemption)</q-tooltip>
          </q-btn>
        </q-td>
      </template>
      <template v-slot:body-cell-error="props">
        <q-td :props="props">
          <div v-if="props.row.error" class="row items-center q-gutter-xs">
            <span class="text-negative text-caption" style="max-width:240px;word-break:break-word">{{ props.row.error }}</span>
            <q-btn v-if="props.row.status === 'failed'" flat dense size="xs" color="primary"
                   icon="refresh" @click="retryFailedRow(props.row)">
              <q-tooltip>Renvoyer ce destinataire (réessaie l'envoi SMTP avec 3 tentatives)</q-tooltip>
            </q-btn>
          </div>
        </q-td>
      </template>
    </q-table>

    <div v-if="!campaign && !loading" class="text-center q-pa-xl text-grey-7">
      <q-icon name="error_outline" size="48px" />
      <div class="text-h6 q-mt-md">Campagne introuvable</div>
    </div>

    <!-- Edit-params dialog. Mirrors a subset of the new-campaign wizard
         step 1 fields — everything that's safe to change pre-launch on
         a draft. Saves via PATCH /campaigns/:id which merges into
         campaign.params (the existing values stay if not touched). -->
    <q-dialog v-model="editParamsOpen" persistent>
      <q-card style="min-width: 560px; max-width: 720px">
        <q-card-section class="row items-center q-pb-none">
          <div class="text-h6"><q-icon name="tune" class="q-mr-sm" />Éditer les paramètres</div>
          <q-space />
          <q-btn flat dense round icon="close" v-close-popup />
        </q-card-section>
        <q-card-section>
          <q-form @submit="saveEditParams" class="q-gutter-sm">
            <q-input v-model="editParams.name" label="Nom interne (visible uniquement dans l'admin)" outlined dense />
            <q-input v-model="editParams.subject" label="Sujet du courriel" outlined dense
                     :rules="[v => !!v || 'requis']" />
            <q-input v-model="editParams.from" label="Expéditeur (From)" outlined dense
                     placeholder="TARGO <noreply@targo.ca>" />
            <div class="row q-col-gutter-sm">
              <q-input v-model="editParams.amount" label="Montant affiché" outlined dense
                       class="col-12 col-sm-4" placeholder="60 $" />
              <q-input v-model.number="editParams.commitment_months" type="number" min="1"
                       label="Engagement (mois)" outlined dense class="col-12 col-sm-4" />
              <q-input v-model="editParams.expiry" label="Expiration (texte affiché)" outlined dense
                       class="col-12 col-sm-4" placeholder="31 décembre 2026" />
            </div>
            <div class="row q-col-gutter-sm">
              <q-select v-model="editParams.template_fr" :options="frTemplateOptions" emit-value map-options
                        label="🇫🇷 Template français" outlined dense class="col-12 col-sm-6"
                        :loading="loadingTemplates" @popup-show="loadTemplateLists" />
              <q-select v-model="editParams.template_en" :options="enTemplateOptions" emit-value map-options
                        label="🇺🇸 Template anglais" outlined dense class="col-12 col-sm-6"
                        :loading="loadingTemplates" @popup-show="loadTemplateLists" />
            </div>
            <div class="text-caption text-grey-7 q-mt-sm">
              <q-icon name="info" size="14px" /> Pour modifier le <strong>contenu</strong> du courriel
              (texte, mise en page, conditions, etc.), clique <strong>"Éditer le template"</strong>
              à côté — ça ouvre l'éditeur Unlayer dans un nouvel onglet. Les modifications de template
              s'appliquent à <strong>toutes les campagnes</strong> qui utilisent ce template ; pour
              une version unique à cette campagne, crée d'abord un template variant
              (<code>+ Nouveau</code> dans l'éditeur) et sélectionne-le ici.
            </div>
            <div class="row q-mt-md">
              <q-space />
              <q-btn flat label="Annuler" v-close-popup class="q-mr-sm" />
              <q-btn unelevated color="primary" type="submit" icon="save" label="Enregistrer"
                     :loading="savingParams" />
            </div>
          </q-form>
        </q-card-section>
      </q-card>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useQuasar } from 'quasar'
import { getCampaign, sendCampaign, campaignSseUrl, campaignReportCsvUrl, retryRecipient, createReminderCampaign, updateCampaign, listTemplates } from 'src/api/campaigns'

const route = useRoute()
const router = useRouter()
const $q = useQuasar()
const id = route.params.id
const campaign = ref(null)
const loading = ref(true)
const resending = ref(false)
const creatingReminder = ref(false)

// Edit-params dialog state. editParams is a snapshot of campaign.params
// that the form mutates; on save we PATCH only that subset back to the
// hub, which merges over the existing params on disk.
const editParamsOpen = ref(false)
const savingParams = ref(false)
const editParams = ref({
  name: '', subject: '', from: '', amount: '', commitment_months: 3,
  expiry: '', template_fr: 'gift-email-fr', template_en: 'gift-email-en',
})

// Template lists for the FR/EN dropdowns in the dialog. Refreshed lazily
// when the dropdown opens so newly-created templates show up without a
// page reload.
const loadingTemplates = ref(false)
const frTemplateOptions = ref([{ label: 'gift-email-fr', value: 'gift-email-fr' }])
const enTemplateOptions = ref([{ label: 'gift-email-en', value: 'gift-email-en' }])
async function loadTemplateLists () {
  loadingTemplates.value = true
  try {
    const tpls = await listTemplates()
    const opt = (t, preferred) => ({
      label: preferred ? t.name : `${t.name} · sans suffixe de langue`,
      value: t.name,
    })
    const fr = [], en = []
    for (const t of tpls) {
      if (!t?.name) continue
      if (t.name.endsWith('-fr')) fr.push(opt(t, true))
      else if (t.name.endsWith('-en')) en.push(opt(t, true))
      else { fr.push(opt(t, false)); en.push(opt(t, false)) }
    }
    const sortFn = (a, b) => {
      const aPref = !a.label.includes('sans suffixe')
      const bPref = !b.label.includes('sans suffixe')
      if (aPref !== bPref) return aPref ? -1 : 1
      return a.value.localeCompare(b.value)
    }
    fr.sort(sortFn); en.sort(sortFn)
    if (fr.length) frTemplateOptions.value = fr
    if (en.length) enTemplateOptions.value = en
  } catch (e) {
    $q.notify({ type: 'warning', message: 'Templates non chargés : ' + e.message })
  } finally {
    loadingTemplates.value = false
  }
}

let es = null

const columns = [
  { name: 'firstname', label: 'Prénom', field: 'firstname', align: 'left' },
  { name: 'lastname', label: 'Nom', field: 'lastname', align: 'left' },
  { name: 'email', label: 'Email', field: 'email', align: 'left' },
  { name: 'civic_address', label: 'Adresse', field: 'civic_address', align: 'left' },
  { name: 'customer', label: 'Client lié', field: 'customer_name', align: 'left' },
  { name: 'gift_url', label: 'Shortlink', field: 'gift_url', align: 'left' },
  { name: 'status', label: 'Statut', field: 'status', align: 'left' },
  { name: 'error', label: 'Erreur', field: 'error', align: 'left' },
]

function counterFor (s) { return campaign.value?.counters?.[s] || 0 }
function statusColor (s) {
  return {
    pending: 'grey-5', queued: 'orange', sent: 'positive', opened: 'positive',
    clicked: 'blue', failed: 'negative', bounced: 'negative',
    draft: 'grey', sending: 'orange', completed: 'positive',
  }[s] || 'grey-5'
}
function statusLabel (s) {
  return {
    pending: 'En attente', queued: 'En file', sent: 'Envoyé', opened: 'Ouvert',
    clicked: 'Cliqué', failed: 'Échec', bounced: 'Rejeté',
    draft: 'Brouillon', sending: 'En cours', completed: 'Terminée',
  }[s] || s
}
function shortLink (u) { return (u || '').replace(/^https?:\/\//, '').slice(0, 28) + ((u || '').length > 35 ? '…' : '') }

// Deep link into Giftbit's admin so the operator can verify redemption
// status manually until the /gifts/{uuid} API polling (task #25) lands.
// The trailing segment of the shortlink URL becomes the search query.
function giftbitAdminUrl (giftUrl) {
  if (!giftUrl) return null
  const code = giftUrl.replace(/\/+$/, '').split('/').pop()
  if (!code || code.length < 4) return null
  return `https://app.giftbit.com/app/rewards?search=${encodeURIComponent(code)}`
}

const reportCsvUrl = computed(() => campaignReportCsvUrl(id))

// Deep link to the template editor for the FR template configured on
// this campaign (most TARGO customers are FR). The editor route also
// accepts no name and lets the user pick from a dropdown.
const templateEditorHref = computed(() => {
  const t = campaign.value?.params?.template_fr || 'gift-email-fr'
  return `/ops/#/campaigns/templates/${encodeURIComponent(t)}`
})

// Eligibility for the "Créer une relance" button: campaign sent at least
// once (not draft), it's not itself a reminder, and at least one recipient
// is in a non-clicked / non-revoked / non-expired state. The hub re-checks
// this when the POST hits — UI gating is purely to hide the button when
// pointless.
const nonClickedCount = computed(() => {
  const rs = campaign.value?.recipients || []
  const now = Date.now()
  return rs.filter(r =>
    !r.excluded
    && !r.gift_link_clicked
    && !r.gift_revoked
    && (!r.gift_expires_at || new Date(r.gift_expires_at).getTime() > now)
    && r.gift_url
    && ['sent', 'opened'].includes(r.status),
  ).length
})
const canCreateReminder = computed(() =>
  campaign.value
  && !campaign.value.reminder_of                        // don't reminder-of-a-reminder
  && ['sending', 'completed'].includes(campaign.value.status)
  && nonClickedCount.value > 0,
)

// Pick a representative expiry to show in the confirmation dialog —
// almost always identical across recipients since they were sent in the
// same worker pass. Fallback to the first non-empty value.
const sampleExpiryDate = computed(() => {
  const r = (campaign.value?.recipients || []).find(r => r.gift_expires_at)
  return r ? new Date(r.gift_expires_at).toLocaleDateString('fr-CA', { day: 'numeric', month: 'long', year: 'numeric' }) : '—'
})

const sentRatio = computed(() => {
  const total = campaign.value?.counters?.total || 1
  const done = counterFor('sent') + counterFor('opened') + counterFor('clicked')
              + counterFor('failed') + counterFor('bounced')
  return Math.min(1, done / total)
})

async function load () {
  loading.value = true
  try { campaign.value = await getCampaign(id) }
  catch (e) { $q.notify({ type: 'negative', message: e.message }) }
  finally { loading.value = false }
}

function subscribeSse () {
  if (es) es.close()
  es = new EventSource(campaignSseUrl(id))
  es.addEventListener('recipient-update', (ev) => {
    const data = JSON.parse(ev.data)
    if (!campaign.value?.recipients) return
    // Apply patch by index; counters will be re-rendered from recipients next refresh
    if (campaign.value.recipients[data.i]) {
      Object.assign(campaign.value.recipients[data.i], data.recipient)
      // Recompute counters in-place for live update
      const counters = { total: campaign.value.recipients.length }
      for (const r of campaign.value.recipients) counters[r.status] = (counters[r.status] || 0) + 1
      campaign.value.counters = counters
    }
  })
  es.addEventListener('campaign-done', () => {
    $q.notify({ type: 'positive', message: 'Campagne terminée' })
    load()
  })
  es.addEventListener('campaign-status', (ev) => {
    const data = JSON.parse(ev.data)
    if (campaign.value) campaign.value.status = data.status
  })
}

async function relaunch () {
  resending.value = true
  try {
    await sendCampaign(id)
    await load()
    subscribeSse()
  } catch (e) {
    $q.notify({ type: 'negative', message: e.message })
  } finally {
    resending.value = false
  }
}

// Re-send a single failed recipient. The hub resets status pending and
// re-fires the worker which retries up to 3 times with backoff. UI live
// updates via the SSE channel — no manual reload needed.
async function retryFailedRow (row) {
  const rowIdx = (campaign.value?.recipients || []).indexOf(row)
  if (rowIdx < 0) return
  try {
    await retryRecipient(id, rowIdx)
    $q.notify({ type: 'positive', message: `Renvoi en cours pour ${row.firstname || row.email}…` })
  } catch (e) {
    $q.notify({ type: 'negative', message: 'Renvoi impossible : ' + e.message })
  }
}

// Open the params dialog, snapshotting current campaign.params + name
// into the editable form. Lazily load the template lists so the
// dropdowns are populated when the dialog mounts.
function openEditParams () {
  const p = campaign.value?.params || {}
  editParams.value = {
    name:              campaign.value?.name || '',
    subject:           p.subject || '',
    from:              p.from || '',
    amount:            p.amount || '',
    commitment_months: p.commitment_months || 3,
    expiry:            p.expiry || '',
    template_fr:       p.template_fr || 'gift-email-fr',
    template_en:       p.template_en || 'gift-email-en',
  }
  editParamsOpen.value = true
  loadTemplateLists()
}

// PATCH the campaign with the new name + params. Hub merges params over
// the existing ones (spread) so unspecified fields stay put. Live UI
// refresh via load() so the recap + counters reflect the new values.
async function saveEditParams () {
  savingParams.value = true
  try {
    await updateCampaign(id, {
      name: editParams.value.name,
      params: {
        subject:           editParams.value.subject,
        from:              editParams.value.from,
        amount:            editParams.value.amount,
        commitment_months: editParams.value.commitment_months,
        expiry:            editParams.value.expiry,
        template_fr:       editParams.value.template_fr,
        template_en:       editParams.value.template_en,
      },
    })
    await load()
    editParamsOpen.value = false
    $q.notify({ type: 'positive', message: 'Paramètres enregistrés' })
  } catch (e) {
    $q.notify({ type: 'negative', message: 'Erreur : ' + e.message })
  } finally {
    savingParams.value = false
  }
}

// Confirmation + creation of the reminder campaign. The hub returns the
// new draft campaign; we navigate to its detail page so the operator can
// review the recipients list and click "Lancer l'envoi" when ready.
function confirmCreateReminder () {
  const n = nonClickedCount.value
  $q.dialog({
    title: 'Créer une campagne de relance ?',
    message: `<div>Une nouvelle campagne <strong>brouillon</strong> sera créée avec les <strong>${n} destinataire(s)</strong> qui n'ont pas encore cliqué le lien-cadeau.<br><br>
              • Mêmes liens Giftbit sous-jacents (le destinataire peut toujours utiliser l'un ou l'autre courriel)<br>
              • Nouveaux liens <code>/g/&lt;token&gt;</code> avec leur propre expiration<br>
              • Template <code>gift-email-reminder-fr</code> / <code>-en</code> (framing d'urgence)<br>
              • Sujet par défaut : "⏰ Dernière chance — ton cadeau TARGO expire bientôt"<br>
              • Expiration affichée : <strong>${sampleExpiryDate.value}</strong><br><br>
              La campagne ne part PAS automatiquement — tu pourras réviser le sujet/template et lancer manuellement.</div>`,
    html: true,
    cancel: { label: 'Annuler', flat: true },
    ok: { label: 'Créer la relance', color: 'orange-9', unelevated: true },
    persistent: true,
  }).onOk(async () => {
    creatingReminder.value = true
    try {
      const saved = await createReminderCampaign(id)
      $q.notify({ type: 'positive', message: `Relance créée — ${n} destinataires` })
      router.push(`/campaigns/${saved.id}`)
    } catch (e) {
      $q.notify({ type: 'negative', message: 'Erreur : ' + e.message })
    } finally {
      creatingReminder.value = false
    }
  })
}

onMounted(async () => {
  await load()
  // Auto-subscribe to SSE if still running (or about to run)
  if (campaign.value && ['draft','sending'].includes(campaign.value.status)) {
    subscribeSse()
  }
})
onBeforeUnmount(() => { if (es) es.close() })
</script>
