<script setup>
/**
 * Counterparty acceptance flow — signature capture.
 * NEW DEPENDENCY (npm): signature_pad
 */
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { useRoute } from "vue-router";
import SignaturePad from "signature_pad";

import BaseAlert from "../../../components/base/BaseAlert.vue";
import BaseBadge from "../../../components/base/BaseBadge.vue";
import BaseButton from "../../../components/base/BaseButton.vue";
import BaseCard from "../../../components/base/BaseCard.vue";
import BaseInputText from "../../../components/base/BaseInputText.vue";
import BaseModal from "../../../components/base/BaseModal.vue";
import BaseTextarea from "../../../components/base/BaseTextarea.vue";
import { useBaseToast } from "../../../composables/useBaseToast.js";
import { apiErrorDetail, ensureCsrf } from "../../../services/api.js";
import { useOffersApi } from "../composables/useOffersApi.js";

const route = useRoute();
const toast = useBaseToast();
const { fetchPublic, acceptPublic, declinePublic } = useOffersApi();

const uuid = computed(() => String(route.params.uuid ?? ""));

const doc = ref(null);
const loading = ref(true);

const signerName = ref("");
const declineReason = ref("");

const acceptOpen = ref(false);
const declineOpen = ref(false);
const busyAccept = ref(false);
const busyDecline = ref(false);

const canvasRef = ref(null);
let pad = null;
let resizeObserver = null;

const canRespond = computed(() => doc.value?.interaction?.can_accept === true || doc.value?.interaction?.can_decline === true);

const statusSeverity = computed(() => {
  const s = doc.value?.status;
  const map = { won: "success", lost: "warn", expired: "danger", viewed: "info", sent: "info", draft: "secondary" };
  return map[s] ?? "secondary";
});

function pngBase64FromDataUrl(url) {
  const i = url.indexOf(",");
  return i >= 0 ? url.slice(i + 1) : url;
}

/** Pricing line descriptions are plain text in the editor; support legacy HTML from older offers. */
function lineDescriptionText(val) {
  if (val == null || val === "") {
    return "";
  }
  const s = String(val);
  if (!/<[a-z][\s\S]*>/i.test(s)) {
    return s;
  }
  if (typeof document === "undefined") {
    return s.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
  }
  const el = document.createElement("div");
  el.innerHTML = s;
  return el.textContent || el.innerText || "";
}

function resizeSignatureCanvas() {
  const canvas = canvasRef.value;
  if (!canvas || !pad) {
    return;
  }
  const ratio = Math.max(window.devicePixelRatio || 1, 1);
  const w = canvas.offsetWidth || 480;
  const h = canvas.offsetHeight || 180;
  canvas.width = w * ratio;
  canvas.height = h * ratio;
  const ctx = canvas.getContext("2d");
  if (ctx) {
    ctx.scale(ratio, ratio);
  }
  pad.clear();
}

async function bootstrap() {
  loading.value = true;
  doc.value = null;
  try {
    doc.value = await fetchPublic(uuid.value);
    signerName.value = doc.value?.contact?.name ?? doc.value?.contact?.email ?? "";
  } catch (err) {
    doc.value = null;
    toast.add({
      severity: "error",
      summary: "Offer not available",
      detail: apiErrorDetail(err),
      life: 4000,
    });
  } finally {
    loading.value = false;
  }
}

async function reloadAfterAction() {
  await bootstrap();
  acceptOpen.value = false;
  declineOpen.value = false;
}

async function onAcceptConfirm() {
  if (!pad || pad.isEmpty()) {
    toast.add({ severity: "warn", summary: "Please sign above", life: 2500 });
    return;
  }
  if (!signerName.value.trim()) {
    toast.add({ severity: "warn", summary: "Signer name required", life: 2500 });
    return;
  }
  busyAccept.value = true;
  try {
    const dataUrl = pad.toDataURL("image/png");
    await acceptPublic(uuid.value, {
      signer_name: signerName.value.trim(),
      signature_png_base64: pngBase64FromDataUrl(dataUrl),
    });
    toast.add({ severity: "success", summary: "Thank you — offer accepted.", life: 3500 });
    await reloadAfterAction();
    pad.clear();
  } catch (err) {
    toast.add({
      severity: "error",
      summary: "Could not submit signature",
      detail: apiErrorDetail(err),
      life: 4000,
    });
  } finally {
    busyAccept.value = false;
  }
}

async function onDeclineConfirm() {
  busyDecline.value = true;
  try {
    await declinePublic(uuid.value, { reason: declineReason.value || null });
    toast.add({ severity: "success", summary: "Response recorded.", life: 2800 });
    await reloadAfterAction();
  } catch (err) {
    toast.add({ severity: "error", summary: apiErrorDetail(err), life: 4000 });
  } finally {
    busyDecline.value = false;
  }
}

watch(acceptOpen, async (visible) => {
  if (!visible) {
    return;
  }
  await nextTick();
  const canvas = canvasRef.value;
  if (!canvas) {
    return;
  }
  if (pad) {
    pad.off();
    pad = null;
  }
  resizeObserver?.disconnect();
  pad = new SignaturePad(canvas, { penColor: "rgb(20,22,34)" });
  resizeSignatureCanvas();
  resizeObserver = new ResizeObserver(() => resizeSignatureCanvas());
  resizeObserver.observe(canvas.parentElement ?? canvas);
});

watch(
  uuid,
  () => {
    void bootstrap();
  },
  { immediate: true },
);

onMounted(async () => {
  try {
    await ensureCsrf();
  } catch {
    // SPA without sanctum in some tests
  }
});

onBeforeUnmount(() => {
  resizeObserver?.disconnect();
  pad?.off?.();
});
</script>

<template>
  <div class="min-h-[100vh] relative bg-surface-50 dark:bg-surface-950 text-surface-800 dark:text-surface-50 py-8 px-4">
    <div
      v-if="doc?.document_background_url"
      class="pointer-events-none absolute inset-0 z-0 bg-cover bg-center bg-no-repeat"
      :style="{ backgroundImage: `url(${doc.document_background_url})` }"
      aria-hidden="true"
    />
    <div class="relative z-10 mx-auto max-w-3xl flex flex-col gap-4">
      <div class="flex items-center gap-3 flex-wrap">
        <BaseBadge
          v-if="doc"
          :value="String(doc.status ?? '')"
          :severity="statusSeverity"
          class="uppercase"
        />
        <span v-if="doc?.offer_number" class="text-sm opacity-75"># {{ doc.offer_number }}</span>
        <span v-if="loading" class="text-sm opacity-75">Loading…</span>
      </div>

      <BaseAlert
        v-if="!loading && !doc"
        severity="error"
        class="border border-surface-200 dark:border-surface-700"
        :closable="false"
      >
        This proposal link could not be loaded.
      </BaseAlert>

      <BaseCard v-if="doc" class="shadow-xl border border-surface-200/90 dark:border-surface-700/90 bg-white/92 dark:bg-surface-900/92 backdrop-blur-md">
        <template #title>
          <span class="text-xl font-semibold">{{ doc.subject }}</span>
        </template>
        <template #content>
          <div class="flex flex-col gap-2 text-sm mb-6 text-surface-600 dark:text-surface-400">
            <div v-if="doc.valid_until"><strong class="text-surface-900 dark:text-surface-100">Valid until</strong> {{ doc.valid_until }}</div>
          </div>
          <div
            class="prose dark:prose-invert prose-sm max-w-none mb-6 text-surface-800 dark:text-surface-50"
            v-html="doc.intro_html ?? ''"
          />

          <div v-for="(sec, i) in doc.sections ?? []" :key="i" class="mb-6">
            <h3 class="text-lg font-semibold mb-2">{{ sec.title }}</h3>
            <div class="text-sm prose dark:prose-invert prose-sm mb-3" v-html="sec.description_html ?? ''"></div>

            <template v-if="sec.is_pricing_block">
              <div
                v-for="(cat, ci) in sec.categories ?? []"
                :key="`pc-${i}-${ci}`"
                class="mb-4 last:mb-0"
              >
                <h4
                  v-if="cat.title"
                  class="text-sm font-semibold text-surface-700 dark:text-surface-200 mb-2"
                >
                  {{ cat.title }}
                </h4>
                <div
                  v-if="cat.description_html"
                  class="text-sm prose dark:prose-invert prose-sm mb-2"
                  v-html="cat.description_html"
                />
                <div
                  class="divide-y divide-surface-200 dark:divide-surface-800 rounded-lg overflow-hidden border border-surface-200 dark:border-surface-800"
                >
                  <div
                    v-for="(row, j) in cat.rows ?? []"
                    :key="`pr-${i}-${ci}-${j}`"
                    class="flex flex-wrap gap-4 justify-between p-3 bg-surface-0 dark:bg-surface-900 text-sm"
                  >
                    <div class="grow min-w-[12rem] whitespace-pre-wrap">{{ lineDescriptionText(row.description_html) }}</div>
                    <div class="text-right tabular-nums space-y-0.5 shrink-0">
                      <div>
                        {{ row.quantity }} × {{ row.unit_price }} {{ row.pricing_label }}
                      </div>
                      <div class="text-surface-500">Net {{ row.line_net_total }}</div>
                    </div>
                  </div>
                </div>
              </div>
            </template>
            <template v-else>
              <div
                class="divide-y divide-surface-200 dark:divide-surface-800 rounded-lg overflow-hidden border border-surface-200 dark:border-surface-800"
              >
                <div
                  v-for="(row, j) in sec.rows ?? []"
                  :key="j"
                  class="flex flex-wrap gap-4 justify-between p-3 bg-surface-0 dark:bg-surface-900 text-sm"
                >
                  <div class="grow min-w-[12rem] whitespace-pre-wrap">{{ lineDescriptionText(row.description_html) }}</div>
                  <div class="text-right tabular-nums space-y-0.5 shrink-0">
                    <div>
                      {{ row.quantity }} × {{ row.unit_price }} {{ row.pricing_label }}
                    </div>
                    <div class="text-surface-500">Net {{ row.line_net_total }}</div>
                  </div>
                </div>
              </div>
            </template>
          </div>

          <div class="rounded-lg border border-dashed border-surface-300 dark:border-surface-700 p-4 mb-6 bg-surface-50/80 dark:bg-surface-900/40">
            <div class="grid gap-3 sm:grid-cols-2 text-sm mb-3">
              <div>
                <div class="text-xs uppercase tracking-wide opacity-70">Prepared for</div>
                <div class="font-medium">{{ doc.customer?.name ?? "—" }}</div>
              </div>
              <div v-if="doc.sender?.legal_name">
                <div class="text-xs uppercase tracking-wide opacity-70">Sender</div>
                <div class="font-medium">{{ doc.sender.legal_name }}</div>
              </div>
            </div>
            <div class="flex flex-wrap justify-between text-sm mb-2">
              <span>Subtotal</span>
              <span class="font-medium">{{ doc.totals?.lines_net_subtotal }}</span>
            </div>
            <div class="flex flex-wrap justify-between text-sm mb-2 text-surface-600 dark:text-surface-400">
              <span>Discount</span>
              <span>− {{ doc.totals?.offer_discount_amount }}</span>
            </div>
            <div class="flex flex-wrap justify-between text-sm mb-2">
              <span>Ex VAT</span>
              <span class="font-medium">{{ doc.totals?.total_excl_vat }}</span>
            </div>
            <div class="flex flex-wrap justify-between text-sm mb-2">
              <span>VAT</span>
              <span class="font-medium">{{ doc.totals?.vat_total }}</span>
            </div>
            <div class="flex flex-wrap justify-between text-base pt-3 border-t border-surface-200 dark:border-surface-700 font-semibold">
              <span>Total</span>
              <span>{{ doc.totals?.grand_total }}</span>
            </div>
          </div>

          <div v-if="doc.signature_url || doc.signer_name" class="rounded-lg border border-surface-200 dark:border-surface-800 p-4 mb-4">
            <div class="text-sm font-semibold mb-2">Signature</div>
            <img v-if="doc.signature_url" :src="doc.signature_url" alt="Signature" class="max-h-32 border border-dashed rounded p-1 bg-white" />
            <div class="text-sm mt-2 opacity-85">{{ doc.signer_name }}</div>
          </div>

          <div class="flex flex-wrap gap-2" v-if="canRespond">
            <BaseButton
              variant="success"
              label="Accept & sign"
              icon="check"
              type="button"
              @click="acceptOpen = true"
            />
            <BaseButton
              label="Decline"
              variant="danger"
              outlined
              icon="times"
              type="button"
              @click="declineOpen = true"
            />
          </div>
          <BaseAlert
            v-else-if="doc && !loading && (doc.status === 'expired' || doc.status === 'lost' || doc.status === 'won')"
            severity="info"
            :closable="false"
            class="mt-4"
          >
            <template v-if="doc.status === 'won'">This proposal has already been accepted.</template>
            <template v-else-if="doc.status === 'lost'">This proposal was declined.</template>
            <template v-else-if="doc.status === 'expired'">This proposal has expired.</template>
            <template v-else>You can no longer respond to this proposal.</template>
          </BaseAlert>
        </template>
      </BaseCard>
    </div>

    <BaseModal
      v-model:visible="acceptOpen"
      title="Accept this proposal"
      width="32rem"
      class="max-w-lg"
      :dismissable-mask="true"
    >
      <div class="flex flex-col gap-3 text-sm">
        <BaseInputText
          v-model="signerName"
          label="Full name"
          placeholder="Printed name"
          fluid
        />
        <span class="font-medium">Signature</span>
        <div class="w-full h-44 rounded-lg border border-surface-300 dark:border-surface-700 overflow-hidden bg-white">
          <canvas ref="canvasRef" class="w-full h-full block touch-none cursor-crosshair" />
        </div>
        <div class="flex gap-2">
          <BaseButton type="button" variant="secondary" text label="Clear" @click="pad?.clear()" />
        </div>
        <BaseButton
          variant="success"
          label="Submit acceptance"
          icon="check"
          type="button"
          :loading="busyAccept"
          @click="onAcceptConfirm"
        />
      </div>
    </BaseModal>

    <BaseModal v-model:visible="declineOpen" title="Decline" width="28rem" class="max-w-md">
      <div class="flex flex-col gap-3 text-sm">
        <BaseTextarea
          v-model="declineReason"
          label="Reason (optional)"
          :rows="4"
          placeholder="Brief note for the seller"
        />
        <BaseButton
          label="Confirm decline"
          icon="times"
          variant="danger"
          type="button"
          :loading="busyDecline"
          @click="onDeclineConfirm"
        />
      </div>
    </BaseModal>
  </div>
</template>
