<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 Badge from "primevue/badge";
import Button from "primevue/button";
import Card from "primevue/card";
import Dialog from "primevue/dialog";
import InputText from "primevue/inputtext";
import Message from "primevue/message";
import Textarea from "primevue/textarea";

import { useToast } from "primevue/usetoast";
import { apiErrorDetail, ensureCsrf } from "../../../services/api.js";
import { useOffersApi } from "../composables/useOffersApi.js";

const route = useRoute();
const toast = useToast();
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;
}

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] bg-surface-50 dark:bg-surface-950 text-surface-800 dark:text-surface-50 py-8 px-4">
    <div class="mx-auto max-w-3xl flex flex-col gap-4">
      <div class="flex items-center gap-3 flex-wrap">
        <Badge v-if="doc" :severity="statusSeverity" class="uppercase">{{ doc.status }}</Badge>
        <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>

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

      <Card v-if="doc" class="shadow border border-surface-200 dark:border-surface-800">
        <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>
            <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]" v-html="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>

          <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">
            <Button label="Accept & sign" icon="pi pi-check" severity="success" type="button" @click="acceptOpen = true" />
            <Button label="Decline" outlined severity="danger" icon="pi pi-times" type="button" @click="declineOpen = true" />
          </div>
          <Message
            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>
          </Message>
        </template>
      </Card>
    </div>

    <Dialog v-model:visible="acceptOpen" header="Accept this proposal" modal class="max-w-lg" dismissable-mask>
      <div class="flex flex-col gap-3 text-sm">
        <label class="flex flex-col gap-1">
          <span class="font-medium">Full name</span>
          <InputText v-model="signerName" placeholder="Printed name" />
        </label>
        <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">
          <Button type="button" text label="Clear" @click="pad?.clear()" />
        </div>
        <Button
          label="Submit acceptance"
          icon="pi pi-check"
          severity="success"
          type="button"
          :loading="busyAccept"
          @click="onAcceptConfirm"
        />
      </div>
    </Dialog>

    <Dialog v-model:visible="declineOpen" header="Decline" modal class="max-w-md">
      <div class="flex flex-col gap-3 text-sm">
        <label class="flex flex-col gap-1">
          <span class="opacity-80">Reason (optional)</span>
          <Textarea v-model="declineReason" rows="4" placeholder="Brief note for the seller" />
        </label>
        <Button label="Confirm decline" icon="pi pi-times" severity="danger" type="button" :loading="busyDecline" @click="onDeclineConfirm" />
      </div>
    </Dialog>
  </div>
</template>
