<script setup>
import BaseButton from "../../../components/base/BaseButton.vue";
import BaseIndex from "../../../components/base/BaseIndex.vue";
import draggable from "vuedraggable/src/vuedraggable.js";
import { computed, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";

import Badge from "primevue/badge";
import Button from "primevue/button";
import Card from "primevue/card";
import Checkbox from "primevue/checkbox";
import Dialog from "primevue/dialog";
import Dropdown from "primevue/dropdown";
import Editor from "primevue/editor";
import InputNumber from "primevue/inputnumber";
import InputText from "primevue/inputtext";
import Toolbar from "primevue/toolbar";
import ToggleButton from "primevue/togglebutton";

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

import { useOffersApi } from "../composables/useOffersApi.js";

/** PrimeVue Editor → Quill. NEW DEPENDENCY (npm): quill */

const route = useRoute();
const router = useRouter();
const toast = useToast();

const {
  getOffer,
  updateOffer,
  sendOffer,
  editorMeta,
  downloadPdf,
  convertToProject,
  listCatalog,
} = useOffersApi();

const offerId = computed(() => String(route.params.id ?? ""));
const model = ref(null);
const preview = ref(false);
const saving = ref(false);

const pricingOptions = [
  { label: "One-time", value: "one_time" },
  { label: "Weekly", value: "weekly" },
  { label: "Monthly", value: "monthly" },
  { label: "Hourly", value: "hourly" },
  { label: "Custom unit", value: "custom_unit" },
];

const discountOptions = [
  { label: "None", value: "none" },
  { label: "Percent", value: "percent" },
  { label: "Fixed", value: "fixed" },
];

const vatChoices = ref([21, 9, 0]);

const catalogDialog = ref(false);
const catalogRows = ref([]);
const catalogSearch = ref("");
const activePick = ref({ si: 0, ri: 0 });

const sendDialog = ref(false);
const sendChannels = ref({ link: true, pdf: false, email: false });
const emailTo = ref("");
const emailSubject = ref("");
const converting = ref(false);

const customerOptions = ref([]);
const contactOptions = ref([]);

let saveTimer = null;

function stampKeys(m) {
  (m?.sections ?? []).forEach((sec, si) => {
    sec._key ??= sec.id ?? `ns-${si}-${crypto.randomUUID()}`;
    (sec.rows ?? []).forEach((row, ri) => {
      row._key ??= row.id ?? `nr-${si}-${ri}-${crypto.randomUUID()}`;
    });
  });
}

function normalizePayload(src) {
  const m = structuredClone(src);
  m.sections = (m.sections ?? []).map((sec, si) => ({
    ...sec,
    title: sec.title ?? `Section ${si + 1}`,
    description_html: sec.description_html ?? "",
    sort_order: si,
    rows: (sec.rows ?? []).map((row, ri) => ({
      ...row,
      pricing_type: row.pricing_type ?? "one_time",
      discount_type: row.discount_type ?? "none",
      discount_value: Number(row.discount_value ?? 0),
      quantity: Number(row.quantity ?? 1),
      unit_price: Number(row.unit_price ?? 0),
      vat_rate: Number(row.vat_rate ?? 21),
      description_html: row.description_html ?? "<p></p>",
      custom_unit_label: row.custom_unit_label ?? "",
      sort_order: ri,
    })),
  }));
  stampKeys(m);
  return m;
}

function isEditable() {
  if (!model.value) return false;
  if (model.value.is_template) return true;
  return model.value.status === "draft";
}

function scheduleSave() {
  if (!isEditable()) return;
  if (saveTimer) clearTimeout(saveTimer);
  saveTimer = setTimeout(() => {
    saveTimer = null;
    persist();
  }, 700);
}

watch(
  () => (model.value ? JSON.stringify(model.value) : ""),
  () => {
    scheduleSave();
  },
);

async function refresh() {
  const data = await getOffer(offerId.value);
  model.value = normalizePayload(data);
}

async function persist() {
  if (!model.value || !isEditable()) return;
  saving.value = true;
  try {
    const p = normalizePayload(model.value);
    const res = await updateOffer(offerId.value, {
      subject: p.subject,
      intro_html: p.intro_html,
      customer_id: p.customer_id ?? null,
      contact_id: p.contact_id ?? null,
      valid_until: p.valid_until || null,
      discount_type: p.discount_type ?? "none",
      discount_value: p.discount_value ?? 0,
      is_template: !!p.is_template,
      template_name: p.template_name ?? null,
      sections: p.sections.map((s) => ({
        title: s.title,
        description_html: s.description_html,
        sort_order: s.sort_order,
        rows: s.rows.map((r) => ({
          description_html: r.description_html,
          pricing_type: r.pricing_type,
          custom_unit_label: r.custom_unit_label || null,
          quantity: r.quantity,
          unit_price: r.unit_price,
          discount_type: r.discount_type,
          discount_value: r.discount_value,
          vat_rate: r.vat_rate,
          sort_order: r.sort_order,
          offer_service_catalog_item_id: r.offer_service_catalog_item_id ?? null,
        })),
      })),
    });
    model.value = normalizePayload(res);
  } finally {
    saving.value = false;
  }
}

function addSection() {
  model.value?.sections.push({
    title: `Section ${(model.value.sections.length || 0) + 1}`,
    description_html: "",
    rows: [],
  });
  stampKeys(model.value);
  scheduleSave();
}

function addRow(si) {
  const sec = model.value?.sections?.[si];
  if (!sec) return;
  sec.rows.push({
    description_html: "<p>Line item</p>",
    pricing_type: "one_time",
    quantity: 1,
    unit_price: 0,
    discount_type: "none",
    discount_value: 0,
    vat_rate: vatChoices.value[0] ?? 21,
    custom_unit_label: "",
  });
  stampKeys(model.value);
  scheduleSave();
}

async function openCatalog(si, ri) {
  activePick.value = { si, ri };
  catalogSearch.value = "";
  await loadCatalog();
  catalogDialog.value = true;
}

async function loadCatalog() {
  const res = await listCatalog({
    search: catalogSearch.value || undefined,
    per_page: 80,
    include_inactive: false,
  });
  catalogRows.value = res?.data ?? [];
}

function applyCatalogItem(item) {
  const { si, ri } = activePick.value;
  const row = model.value.sections[si].rows[ri];
  const html = item.default_description?.trim()
    ? String(item.default_description)
    : `<p>${item.name}</p>`;
  row.description_html = html;
  row.pricing_type = item.default_pricing_type;
  row.unit_price = item.default_unit_price;
  row.quantity = item.default_quantity;
  row.vat_rate = item.default_vat_rate;
  row.custom_unit_label = item.default_custom_unit_label ?? "";
  row.offer_service_catalog_item_id = item.id;
  catalogDialog.value = false;
  scheduleSave();
}

const totals = computed(() => model.value?.totals ?? {});

const CUSTOMER_LIST_TOAST_KEY = "maestro.offers.customerListUnavailableToast";

async function loadCustomers() {
  try {
    await ensureCsrf();
    const { data } = await api.get("/api/customers", { params: { per_page: 100, sort_by: "name" } });
    customerOptions.value = (data?.data ?? []).map((c) => ({ label: c.name, value: c.id }));
    try {
      if (typeof sessionStorage !== "undefined") {
        sessionStorage.removeItem(CUSTOMER_LIST_TOAST_KEY);
      }
    } catch {
      /* ignore */
    }
  } catch {
    customerOptions.value = [];
    // Customers API may fail when the customers module is off or lacks access; editor still usable without picker.
    try {
      if (typeof sessionStorage !== "undefined" && !sessionStorage.getItem(CUSTOMER_LIST_TOAST_KEY)) {
        sessionStorage.setItem(CUSTOMER_LIST_TOAST_KEY, "1");
        toast.add({
          severity: "warn",
          summary: "Customer list unavailable",
          detail:
            "You can still edit the offer. Enable the Customers module or fix permissions to pick a customer.",
          life: 5000,
        });
      }
    } catch {
      // sessionStorage may be unavailable (private mode); skip dedupe
    }
  }
}

async function onCustomerChange() {
  contactOptions.value = [];
  if (!model.value?.customer_id) {
    model.value.contact_id = null;
    scheduleSave();
    return;
  }
  try {
    await ensureCsrf();
    const { data } = await api.get(`/api/customers/${model.value.customer_id}/contacts`);
    const contacts = data?.data ?? data ?? [];
    contactOptions.value = contacts.map((c) => ({
      label: `${c.first_name} ${c.last_name}`,
      value: c.id,
    }));
  } catch {
    contactOptions.value = [];
    toast.add({
      severity: "warn",
      summary: "Contacts could not be loaded",
      life: 3500,
    });
  }
  scheduleSave();
}

async function onSend() {
  await persist();
  const channels = [];
  if (sendChannels.value.link) channels.push("link");
  if (sendChannels.value.pdf) channels.push("pdf");
  if (sendChannels.value.email) channels.push("email");
  try {
    const res = await sendOffer(offerId.value, {
      channels,
      email:
        sendChannels.value.email && emailTo.value
          ? { to: emailTo.value, subject: emailSubject.value || undefined }
          : undefined,
    });
    toast.add({ severity: "success", summary: "Offer sent", life: 2800 });
    if (res?.public_url) {
      await navigator.clipboard.writeText(res.public_url);
      toast.add({ severity: "info", summary: "Public link copied", life: 2200 });
    }
    sendDialog.value = false;
    await refresh();
  } catch {
    toast.add({ severity: "error", summary: "Send failed", life: 3500 });
  }
}

async function onPdf() {
  const blob = await downloadPdf(offerId.value);
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `offer-${model.value?.offer_number ?? offerId.value}.pdf`;
  a.click();
  URL.revokeObjectURL(url);
}

async function onConvert() {
  converting.value = true;
  try {
    await convertToProject(offerId.value, { name: model.value?.subject });
    toast.add({ severity: "success", summary: "Project created", life: 2800 });
    await refresh();
  } catch {
    toast.add({ severity: "error", summary: "Conversion failed", life: 3600 });
  } finally {
    converting.value = false;
  }
}

onMounted(async () => {
  try {
    const meta = await editorMeta();
    if (Array.isArray(meta?.vat_rate_presets)) {
      vatChoices.value = meta.vat_rate_presets.map((x) => Number(x));
    }
    await loadCustomers();
    await refresh();
    if (model.value?.customer_id) {
      await onCustomerChange();
    }
  } catch (e) {
    const detail = e?.response?.data?.message || e?.message || "";
    console.error("[OfferEditor] mount failed", e);
    toast.add({ severity: "error", summary: "Could not load offer", detail, life: 5000 });
    router.push("/offers");
  }
});
</script>

<template>
  <BaseIndex v-if="model" flush hide-header>
    <Toolbar class="mb-3 border border-surface-200 dark:border-surface-700 rounded-xl">
      <template #start>
        <div class="flex flex-wrap items-center gap-2">
          <Button icon="pi pi-arrow-left" text type="button" @click="router.push('/offers')" />
          <span class="font-semibold">{{ model.offer_number }}</span>
          <Badge :value="model.status" severity="secondary" />
          <span v-if="saving" class="text-xs text-surface-500">Saving…</span>
        </div>
      </template>
      <template #end>
        <div class="flex flex-wrap gap-2">
          <ToggleButton v-model="preview" on-label="Preview" off-label="Edit" class="min-w-[7rem]" />
          <Button
            v-if="model.status === 'draft' && !model.is_template"
            label="Send"
            icon="pi pi-send"
            severity="primary"
            type="button"
            @click="sendDialog = true"
          />
          <Button label="PDF" icon="pi pi-download" outlined type="button" @click="onPdf" />
          <Button
            v-if="model.status === 'won' && !model.hour_project_id"
            label="Convert to project"
            icon="pi pi-briefcase"
            severity="success"
            type="button"
            :loading="converting"
            @click="onConvert"
          />
        </div>
      </template>
    </Toolbar>

    <div v-if="!preview" class="grid gap-4 md:grid-cols-3 mb-4">
      <Card class="md:col-span-2 shadow-sm border border-surface-200 dark:border-surface-700">
        <template #title>Header</template>
        <template #content>
          <div class="flex flex-col gap-3">
            <label class="flex flex-col gap-1 text-sm font-medium">
              Subject
              <InputText v-model="model.subject" :disabled="!isEditable()" @update:model-value="scheduleSave" />
            </label>
            <div class="grid gap-3 md:grid-cols-2">
              <label class="flex flex-col gap-1 text-sm font-medium">
                Customer
                <Dropdown
                  v-model="model.customer_id"
                  :options="customerOptions"
                  option-label="label"
                  option-value="value"
                  placeholder="Optional"
                  show-clear
                  :disabled="!isEditable()"
                  @change="onCustomerChange"
                />
              </label>
              <label class="flex flex-col gap-1 text-sm font-medium">
                Contact
                <Dropdown
                  v-model="model.contact_id"
                  :options="contactOptions"
                  option-label="label"
                  option-value="value"
                  placeholder="Optional"
                  show-clear
                  :disabled="!isEditable()"
                  @change="scheduleSave"
                />
              </label>
            </div>
            <label class="flex flex-col gap-1 text-sm font-medium">
              Valid until
              <InputText v-model="model.valid_until" type="date" :disabled="!isEditable()" @change="scheduleSave" />
            </label>
            <div class="flex flex-wrap gap-2 items-end">
              <label class="flex flex-col gap-1 text-sm font-medium flex-1 min-w-[8rem]">
                Offer discount type
                <Dropdown
                  v-model="model.discount_type"
                  :options="discountOptions"
                  option-label="label"
                  option-value="value"
                  @change="scheduleSave"
                />
              </label>
              <label class="flex flex-col gap-1 text-sm font-medium flex-1 min-w-[8rem]">
                Amount
                <InputNumber v-model="model.discount_value" fluid @update:model-value="scheduleSave" />
              </label>
            </div>
            <div class="flex items-center gap-2">
              <Checkbox
                v-model="model.is_template"
                binary
                input-id="tpl"
                :disabled="model.status !== 'draft'"
                @update:model-value="scheduleSave"
              />
              <label for="tpl" class="text-sm">Template library</label>
            </div>
            <label v-if="model.is_template" class="flex flex-col gap-1 text-sm font-medium">
              Template name
              <InputText v-model="model.template_name" @update:model-value="scheduleSave" />
            </label>
          </div>
        </template>
      </Card>

      <Card class="shadow-sm border border-surface-200 dark:border-surface-700">
        <template #title>Intro</template>
        <template #content>
          <Editor
            v-model="model.intro_html"
            editor-style="min-height: 200px"
            :readonly="!isEditable()"
            @update:model-value="scheduleSave"
          />
        </template>
      </Card>
    </div>

    <div
      v-else
      class="rounded-xl border border-surface-200 dark:border-surface-700 bg-surface-0 dark:bg-surface-900 p-6 mb-4"
    >
      <div
        class="prose dark:prose-invert max-w-none text-surface-800 dark:text-surface-50"
        v-html="model.intro_html ?? ''"
      ></div>
    </div>

    <div v-if="!preview" class="flex flex-col gap-3 pb-48">
      <draggable v-model="model.sections" item-key="_key" handle=".drag-sec" :disabled="!isEditable()" @end="scheduleSave">
        <template #item="{ element: sec, index: si }">
          <Card class="shadow-sm border border-surface-200 dark:border-surface-700">
            <template #title>
              <div class="flex items-center gap-2">
                <i class="pi pi-bars drag-sec cursor-grab text-surface-400" />
                <InputText v-model="sec.title" class="flex-1" @update:model-value="scheduleSave" />
              </div>
            </template>
            <template #content>
              <draggable v-model="sec.rows" item-key="_key" handle=".drag-row" :disabled="!isEditable()" @end="scheduleSave">
                <template #item="{ element: row, index: ri }">
                  <div class="border border-dashed border-surface-200 dark:border-surface-700 rounded-lg p-3 mb-2">
                    <div class="flex flex-wrap gap-2 items-center mb-2">
                      <i class="pi pi-bars drag-row cursor-grab text-surface-400" />
                      <Dropdown
                        v-model="row.pricing_type"
                        class="min-w-[10rem]"
                        :options="pricingOptions"
                        option-label="label"
                        option-value="value"
                        @change="scheduleSave"
                      />
                      <Button size="small" type="button" text label="Catalog" icon="pi pi-search" @click="openCatalog(si, ri)" />
                    </div>
                    <Editor
                      v-model="row.description_html"
                      editor-style="min-height: 96px"
                      :readonly="!isEditable()"
                      @update:model-value="scheduleSave"
                    />
                    <div class="grid gap-2 md:grid-cols-4 mt-2">
                      <InputNumber v-model="row.quantity" placeholder="Qty" fluid @update:model-value="scheduleSave" />
                      <InputNumber v-model="row.unit_price" mode="decimal" fluid @update:model-value="scheduleSave" />
                      <Dropdown
                        v-model="row.vat_rate"
                        fluid
                        :options="vatChoices.map((x) => ({ label: `${x}%`, value: x }))"
                        option-label="label"
                        option-value="value"
                        @change="scheduleSave"
                      />
                      <InputText
                        v-if="row.pricing_type === 'custom_unit'"
                        v-model="row.custom_unit_label"
                        placeholder="Custom unit label"
                        @update:model-value="scheduleSave"
                      />
                    </div>
                    <div class="grid gap-2 md:grid-cols-2 mt-2">
                      <Dropdown
                        v-model="row.discount_type"
                        :options="discountOptions"
                        option-label="label"
                        option-value="value"
                        @change="scheduleSave"
                      />
                      <InputNumber v-model="row.discount_value" fluid @update:model-value="scheduleSave" />
                    </div>
                  </div>
                </template>
              </draggable>
              <BaseButton v-if="isEditable()" label="Add row" icon="pi pi-plus" variant="secondary" class="mt-2" @click="addRow(si)" />
            </template>
          </Card>
        </template>
      </draggable>
      <Button v-if="isEditable()" label="Add section" icon="pi pi-plus" outlined fluid type="button" @click="addSection" />
    </div>

    <div class="sticky bottom-3 border border-surface-200 dark:border-surface-800 bg-surface-0/95 dark:bg-surface-900/95 backdrop-blur-md rounded-xl p-4 shadow-lg">
      <div class="grid gap-2 md:grid-cols-3 text-sm">
        <div>
          Subtotal:
          <strong>{{ totals.lines_net_subtotal }}</strong>
        </div>
        <div>
          Discount:
          <strong>{{ totals.offer_discount_amount }}</strong>
        </div>
        <div>
          Ex VAT:
          <strong>{{ totals.total_excl_vat }}</strong>
        </div>
        <div>
          VAT:
          <strong>{{ totals.vat_total }}</strong>
        </div>
        <div class="md:col-span-2 text-lg">
          Total:
          <strong>{{ totals.grand_total }}</strong>
        </div>
      </div>
    </div>

    <Dialog v-model:visible="catalogDialog" header="Pick from catalog" modal class="max-w-xl" @show="loadCatalog">
      <div class="flex gap-2 mb-3">
        <InputText v-model="catalogSearch" class="flex-1" placeholder="Search" @keyup.enter="loadCatalog" />
        <Button type="button" label="Search" @click="loadCatalog" />
      </div>
      <div class="max-h-72 overflow-auto flex flex-col gap-1">
        <Button
          v-for="item in catalogRows"
          :key="item.id"
          class="justify-start"
          text
          type="button"
          :label="item.name"
          @click="applyCatalogItem(item)"
        />
      </div>
    </Dialog>

    <Dialog v-model:visible="sendDialog" header="Send" modal class="max-w-md">
      <div class="flex flex-col gap-3 text-sm">
        <div class="flex items-center gap-2">
          <Checkbox v-model="sendChannels.link" binary input-id="ch-link" />
          <label for="ch-link">Copy public link after send</label>
        </div>
        <div class="flex items-center gap-2">
          <Checkbox v-model="sendChannels.pdf" binary input-id="ch-pdf" />
          <label for="ch-pdf">Expose PDF shortcut (still requires login)</label>
        </div>
        <div class="flex items-center gap-2">
          <Checkbox v-model="sendChannels.email" binary input-id="ch-mail" />
          <label for="ch-mail">Email</label>
        </div>
        <template v-if="sendChannels.email">
          <InputText v-model="emailTo" placeholder="To" />
          <InputText v-model="emailSubject" placeholder="Subject" />
        </template>
        <Button label="Send" icon="pi pi-send" type="button" severity="primary" @click="onSend" />
      </div>
    </Dialog>
  </BaseIndex>
</template>
