<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from "vue";
import { apiDelete, apiGet, apiPost, apiPut } from "../api";
import FormStatus from "../components/FormStatus.vue";
import PageHeader from "../components/PageHeader.vue";
import SearchSelect from "../components/SearchSelect.vue";
import { decimalString, formatDecimal, formatDecimalInput, normalizeTaxRate, taxRateOptions } from "../format";
import { reserveNextNumber } from "../number-ranges";
import { liveUpdateState } from "../realtime";
import type { Activity, Customer, Item, Quote, QuoteItem } from "../types";

const emptyItem = (): QuoteItem => ({ line_kind: "item", item_id: "", activity_id: null, description: "", quantity: "1", unit_price: "0", original_unit_price: null, discount_percent: "0", tax_rate: "19" });
const emptyForm = () => ({ quote_number: "", customer_id: "", status: "draft", valid_until: null as string | null, cash_discount_term_id: null as string | null, customer_discount_percent: "0", notes: "", items: [emptyItem()] });

const quotes = ref<Quote[]>([]);
const customers = ref<Customer[]>([]);
const items = ref<Item[]>([]);
const activities = ref<Activity[]>([]);
const selectedId = ref<string | null>(null);
const form = reactive(emptyForm());
const status = ref("");
const kind = ref<"info" | "success" | "error">("info");
const selectedQuote = computed(() => quotes.value.find((quote) => quote.id === selectedId.value));
const search = ref("");
const editingLineIndex = ref(0);
const activeSection = ref<"party" | "positions">("party");
const readOnly = computed(() => ["invoice_created", "cancelled"].includes(form.status));
const readOnlyText = computed(() => form.status === "invoice_created" ? "Rechnung erstellt" : form.status === "cancelled" ? "Storniert" : "");
const customerName = (customerId: string) => customers.value.find((customer) => customer.id === customerId)?.name ?? customerId;
const filteredQuotes = computed(() => {
  const needle = search.value.trim().toLowerCase();
  if (!needle) return quotes.value;
  return quotes.value.filter((quote) =>
    `${quote.quote_number} ${customerName(quote.customer_id)} ${quote.status}`.toLowerCase().includes(needle)
  );
});

async function load() {
  const [quoteResult, customerResult, itemResult, activityResult] = await Promise.all([
    apiGet<Quote[]>("/api/v1/quotes"),
    apiGet<Customer[]>("/api/v1/customers"),
    apiGet<Item[]>("/api/v1/items"),
    apiGet<Activity[]>("/api/v1/activities")
  ]);
  if (quoteResult.ok) quotes.value = quoteResult.data; else { status.value = quoteResult.message; kind.value = "error"; }
  if (customerResult.ok) customers.value = customerResult.data.filter((customer) => customer.status === "active");
  if (itemResult.ok) items.value = itemResult.data.filter((item) => item.status === "active");
  if (activityResult.ok) {
    activities.value = activityResult.data.filter((activity) => activity.status === "active");
    refreshActivityLineDefaults();
  }
}

async function createNew() {
  selectedId.value = null;
  Object.assign(form, emptyForm());
  editingLineIndex.value = 0;
  activeSection.value = "party";
  form.quote_number = (await reserveNextNumber("quotes")) ?? "";
}

function select(quote: Quote) {
  selectedId.value = quote.id;
  Object.assign(form, { ...quote, items: quote.items.map((item) => ({ ...item, line_kind: item.line_kind ?? "item", tax_rate: normalizeTaxRate(item.tax_rate) })) });
  editingLineIndex.value = -1;
  activeSection.value = "party";
}

function addLine() {
  if (readOnly.value) return;
  activeSection.value = "positions";
  const draftIndex = form.items.findIndex((line) => !shouldSendLine(line));
  if (draftIndex >= 0) {
    editingLineIndex.value = draftIndex;
    return;
  }
  form.items.push(emptyItem());
  editingLineIndex.value = form.items.length - 1;
}

function removeLine(index: number) {
  if (readOnly.value) return;
  form.items.splice(index, 1);
  if (form.items.length === 0) {
    form.items.push(emptyItem());
    editingLineIndex.value = 0;
    return;
  }
  editingLineIndex.value = -1;
}

function applyItemDefaults(line: QuoteItem) {
  const item = items.value.find((record) => record.id === line.item_id);
  if (!item) return;
  line.description = item.name;
  applyLinePrice(line, item.default_sales_price);
  line.tax_rate = normalizeTaxRate(item.tax_rate);
}

function applyLinePrice(line: QuoteItem, price: string | number | null | undefined) {
  const normalized = decimalString(price);
  line.unit_price = formatDecimal(normalized);
  line.original_unit_price = normalized;
}

function refreshActivityLineDefaults() {
  for (const line of form.items) {
    if (line.line_kind !== "activity" || !line.activity_id) continue;
    const activity = activities.value.find((record) => record.id === line.activity_id);
    if (!activity?.default_sales_price) continue;
    const current = decimalString(line.unit_price);
    const original = line.original_unit_price === null ? "0" : decimalString(line.original_unit_price);
    if (current === original || current === "0") applyLinePrice(line, activity.default_sales_price);
  }
}

const lineSourceOptions = computed(() => [
  ...items.value.map((item) => ({ id: `item:${item.id}`, number: item.item_number, name: item.name })),
  ...activities.value.map((activity) => ({ id: `activity:${activity.id}`, number: activity.activity_number, name: activity.title }))
]);

function lineSourceValue(line: QuoteItem) {
  return line.line_kind === "activity" ? `activity:${line.activity_id ?? ""}` : `item:${line.item_id ?? ""}`;
}

function selectLineSource(line: QuoteItem, value: string | null) {
  if (!value) return;
  const [kind, id] = value.split(":");
  if (kind === "activity") {
    const activity = activities.value.find((record) => record.id === id);
    if (!activity) return;
    line.line_kind = "activity";
    line.activity_id = id;
    line.item_id = null;
    line.description = activity.title;
    applyLinePrice(line, activity.default_sales_price);
    line.tax_rate = "19";
    return;
  }
  const item = items.value.find((record) => record.id === id);
  if (!item) return;
  line.line_kind = "item";
  line.item_id = id;
  line.activity_id = null;
  applyItemDefaults(line);
}

function applyCustomerDefaults(customerId: string | null) {
  if (!customerId) return;
  const customer = customers.value.find((record) => record.id === customerId);
  if (!customer) return;
  form.customer_discount_percent = customer.standard_discount_percent || "0";
  form.cash_discount_term_id = customer.cash_discount_term_id ?? null;
}

function lineName(line: QuoteItem) {
  if (line.line_kind === "activity") {
    const activity = activities.value.find((record) => record.id === line.activity_id);
    return activity ? `${activity.activity_number ?? "Aktivität"} - ${activity.title}` : "Keine Aktivität gewählt";
  }
  if (!line.item_id) return "Kein Artikel gewählt";
  const item = items.value.find((record) => record.id === line.item_id);
  return item ? `${item.item_number} - ${item.name}` : line.item_id;
}

function lineUnit(line: QuoteItem) {
  if (line.line_kind === "activity") {
    const category = activities.value.find((record) => record.id === line.activity_id)?.price_category;
    if (category === "tag") return "Tag";
    if (category === "pauschal") return "Pauschal";
    return "h";
  }
  return items.value.find((record) => record.id === line.item_id)?.unit ?? "";
}

function quantityWithUnit(line: QuoteItem) {
  const unit = lineUnit(line);
  return unit ? `${line.quantity} ${unit}` : line.quantity;
}

function optionalId(value: string | null | undefined) {
  return value && value.trim() ? value : null;
}

function shouldSendLine(line: QuoteItem) {
  const hasSource = line.line_kind === "activity" ? optionalId(line.activity_id) !== null : optionalId(line.item_id) !== null;
  if (hasSource) return true;
  return Boolean(line.description.trim()) || decimalString(line.unit_price) !== "0";
}

function lineIdentity(line: QuoteItem) {
  const source = line.line_kind === "activity" ? `activity:${optionalId(line.activity_id) ?? ""}` : `item:${optionalId(line.item_id) ?? ""}`;
  return [
    source,
    line.description.trim(),
    decimalString(line.unit_price),
    decimalString(line.discount_percent),
    normalizeTaxRate(line.tax_rate)
  ].join("|");
}

function ensureUniqueLines() {
  const seen = new Set<string>();
  for (const line of form.items.filter(shouldSendLine)) {
    const key = lineIdentity(line);
    if (seen.has(key)) {
      status.value = "Diese Position ist mit gleichen Bedingungen bereits vorhanden. Bitte die vorhandene Position bearbeiten.";
      kind.value = "error";
      activeSection.value = "positions";
      return false;
    }
    seen.add(key);
  }
  return true;
}

function closeLineEditor() {
  if (!ensureUniqueLines()) return;
  editingLineIndex.value = -1;
}

function isNewLine(line: QuoteItem) {
  return !line.id;
}

function activateSection(section: "party" | "positions") {
  activeSection.value = section;
}

function lineTotal(line: QuoteItem) {
  const total = lineNetTotal(line);
  return Number.isFinite(total) ? total.toLocaleString("de-DE", { minimumFractionDigits: 2, maximumFractionDigits: 4 }) : "-";
}

function lineNetTotal(line: QuoteItem) {
  const quantity = Number(decimalString(line.quantity, "0"));
  const unitPrice = Number(decimalString(line.unit_price, "0"));
  const discount = Number(decimalString(line.discount_percent, "0"));
  return quantity * unitPrice * (1 - discount / 100);
}

function lineTaxTotal(line: QuoteItem) {
  const taxRate = Number(decimalString(line.tax_rate, "0"));
  return lineNetTotal(line) * taxRate / 100;
}

const totals = computed(() => {
  const sendableLines = form.items.filter(shouldSendLine);
  const net = sendableLines.reduce((sum, line) => sum + lineNetTotal(line), 0);
  const tax = sendableLines.reduce((sum, line) => sum + lineTaxTotal(line), 0);
  return { net, tax, gross: net + tax };
});

function money(value: number) {
  return value.toLocaleString("de-DE", { style: "currency", currency: "EUR", minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

async function save() {
  if (readOnly.value) return;
  if (!ensureUniqueLines()) return;
  const payload = {
    ...form,
    customer_discount_percent: decimalString(form.customer_discount_percent),
    items: form.items
      .filter(shouldSendLine)
      .map((item) => ({
        ...item,
        line_kind: item.line_kind ?? "item",
        item_id: item.line_kind === "activity" ? null : optionalId(item.item_id),
        activity_id: item.line_kind === "activity" ? optionalId(item.activity_id) : null,
        quantity: decimalString(item.quantity, "1"),
        unit_price: decimalString(item.unit_price),
        original_unit_price: item.original_unit_price === null ? null : decimalString(item.original_unit_price),
        discount_percent: decimalString(item.discount_percent),
        tax_rate: normalizeTaxRate(item.tax_rate)
      }))
  };
  const result = selectedId.value
    ? await apiPut<Quote>(`/api/v1/quotes/${selectedId.value}`, payload)
    : await apiPost<Quote>("/api/v1/quotes", payload);
  status.value = result.ok ? "Angebot gespeichert." : result.message;
  kind.value = result.ok ? "success" : "error";
  if (result.ok) { selectedId.value = result.data.id; await load(); }
}

async function cancelQuote() {
  if (readOnly.value) return;
  if (!selectedId.value) return;
  const result = await apiDelete(`/api/v1/quotes/${selectedId.value}`);
  status.value = result.ok ? "Angebot storniert." : result.message;
  kind.value = result.ok ? "success" : "error";
  if (result.ok) await load();
}

onMounted(load);
watch(() => liveUpdateState.revision, load);
</script>

<template>
  <PageHeader title="Angebote" description="Angebote mit festen Artikelpositionen und individuellen Preisen." />
  <div class="workspace-split">
    <section class="panel list-panel">
      <div class="section-title"><h2>Angebote</h2><button type="button" class="add-button" title="Neues Angebot" @click="createNew">+</button></div>
      <label class="field list-search"><span>Suche</span><input v-model="search" type="search" placeholder="Angebotsnummer, Kunde oder Status" /></label>
      <button v-for="quote in filteredQuotes" :key="quote.id" type="button" class="list-row" :class="{ selected: selectedId === quote.id }" @click="select(quote)">
        <strong>{{ quote.quote_number }}</strong>
        <span>{{ customerName(quote.customer_id) }}</span>
        <small>{{ quote.status }}</small>
      </button>
      <p v-if="quotes.length === 0" class="empty">Keine Angebote vorhanden.</p>
      <p v-else-if="filteredQuotes.length === 0" class="empty">Keine Treffer.</p>
    </section>
    <section class="panel detail-panel">
      <form class="document-form" @submit.prevent="save">
        <div v-if="readOnly" class="readonly-notice">Dieses Angebot ist schreibgeschützt: {{ readOnlyText }}.</div>
        <div class="accordion-section" :class="{ active: activeSection === 'party' }">
          <button type="button" class="accordion-header" @click="activateSection('party')"><span>Kunde</span><strong>{{ activeSection === 'party' ? "▾" : "▸" }}</strong></button>
          <div v-if="activeSection === 'party'" class="accordion-body form-grid">
            <label class="field"><span>Angebotsnummer</span><div class="readonly-value">{{ form.quote_number || "wird automatisch vergeben" }}</div></label>
            <label class="field"><span>Kunde</span><div v-if="readOnly" class="readonly-value">{{ customerName(form.customer_id) }}</div><SearchSelect v-else v-model="form.customer_id" :options="customers.map((customer) => ({ id: customer.id, number: customer.customer_number, name: customer.name }))" placeholder="Kunden-Nr. oder Name suchen" required @change="applyCustomerDefaults" /></label>
            <label class="field"><span>Status</span><div v-if="readOnly" class="readonly-value">{{ readOnlyText }}</div><select v-else v-model="form.status"><option value="draft">Entwurf</option><option value="sent">Gesendet</option><option value="accepted">Angenommen</option><option value="rejected">Abgelehnt</option><option value="expired">Abgelaufen</option></select></label>
            <label class="field"><span>Gültig bis</span><input v-model="form.valid_until" type="date" :disabled="readOnly" /></label>
            <label class="field"><span>Kundenrabatt %</span><input v-model="form.customer_discount_percent" inputmode="decimal" :disabled="readOnly" /></label>
            <label class="field full-width"><span>Notizen</span><textarea v-model="form.notes" rows="3" :disabled="readOnly" /></label>
          </div>
        </div>
        <div class="accordion-section" :class="{ active: activeSection === 'positions' }">
          <button type="button" class="accordion-header" @click="activateSection('positions')"><span>Positionen</span><strong>{{ activeSection === 'positions' ? "▾" : "▸" }}</strong></button>
          <div v-if="activeSection === 'positions'" class="accordion-body sub-panel">
            <div class="section-title"><h2>Positionen</h2><button v-if="!readOnly" type="button" class="add-button" title="Neue Position" @click="addLine">+</button></div>
            <div class="position-table-header"><span>Position</span><span>Menge</span><span>Einzelpreis</span><span>Gesamtpreis</span><span>Steuer</span><span>Aktion</span></div>
            <div v-for="(line, index) in form.items" :key="index" class="quote-line">
              <template v-if="editingLineIndex !== index && shouldSendLine(line)">
                <div class="position-summary">
                  <strong>{{ lineName(line) }}</strong>
                  <span>{{ quantityWithUnit(line) }}</span>
                  <span>{{ line.unit_price }}</span>
                  <span>{{ lineTotal(line) }}</span>
                  <span>{{ line.tax_rate }} %</span>
                </div>
                <div class="position-row-actions">
                  <button v-if="!readOnly" type="button" class="secondary icon-button" title="Bearbeiten" @click="editingLineIndex = index">✎</button>
                  <button v-if="!readOnly" type="button" class="secondary icon-button" title="Entfernen" @click="removeLine(index)">×</button>
                </div>
              </template>
              <template v-else>
                <label class="field full-width"><span>Artikel oder Aktivität</span><div v-if="readOnly" class="readonly-value">{{ lineName(line) }}</div><SearchSelect v-else :model-value="lineSourceValue(line)" :options="lineSourceOptions" placeholder="Artikel-/Aktivitäts-Nr. oder Name suchen" required @update:model-value="selectLineSource(line, $event)" /></label>
                <label class="field"><span>Menge</span><input v-model="line.quantity" inputmode="decimal" required :disabled="readOnly" @blur="formatDecimalInput(line, 'quantity')" /></label>
                <label class="field"><span>Einheit</span><div class="readonly-value">{{ lineUnit(line) || "-" }}</div></label>
                <label class="field"><span>Preis</span><input v-model="line.unit_price" inputmode="decimal" required :disabled="readOnly" @blur="formatDecimalInput(line, 'unit_price')" /></label>
                <label class="field"><span>Rabatt %</span><input v-model="line.discount_percent" inputmode="decimal" :disabled="readOnly" @blur="formatDecimalInput(line, 'discount_percent')" /></label>
                <label class="field"><span>Steuer %</span><select v-model="line.tax_rate" :disabled="readOnly"><option v-for="rate in taxRateOptions" :key="rate" :value="rate">{{ rate }} %</option></select></label>
                <label class="field full-width"><span>Beschreibung</span><input v-model="line.description" :disabled="readOnly" /></label>
                <div v-if="!readOnly" class="position-actions">
                  <button type="button" class="secondary" @click="removeLine(index)">Entfernen</button>
                  <button type="button" @click="closeLineEditor">{{ isNewLine(line) ? "+" : "Änderung übernehmen" }}</button>
                </div>
              </template>
            </div>
            <div v-if="!readOnly" class="position-add-row"><button type="button" class="add-button" title="Neue Position" @click="addLine">+</button></div>
          </div>
        </div>
        <div v-if="!readOnly" class="form-actions"><button type="submit">Speichern</button><button v-if="selectedQuote" type="button" class="secondary" @click="cancelQuote">Stornieren</button></div>
        <FormStatus :message="status" :kind="kind" />
        <div class="totals-panel">
          <div><span>Netto-Summe</span><strong>{{ money(totals.net) }}</strong></div>
          <div><span>Steuer</span><strong>{{ money(totals.tax) }}</strong></div>
          <div class="grand-total"><span>Gesamtsumme</span><strong>{{ money(totals.gross) }}</strong></div>
        </div>
      </form>
    </section>
  </div>
</template>
