<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, normalizeTaxRate, taxRateOptions } from "../format";
import { reserveNextNumber } from "../number-ranges";
import { liveUpdateState } from "../realtime";
import type { IncomingInvoice, IncomingInvoiceItem, Item, Supplier } from "../types";

const emptyItem = (): IncomingInvoiceItem => ({ item_id: null, description: "", quantity: "1", unit_price: "0", tax_rate: "19" });
const emptyForm = () => ({ invoice_number: "", supplier_id: "", status: "received", cash_discount_term_id: null as string | null, invoice_date: null as string | null, due_at: null as string | null, items: [emptyItem()] });
const invoices = ref<IncomingInvoice[]>([]); const suppliers = ref<Supplier[]>([]); const items = ref<Item[]>([]);
const selectedId = ref<string | null>(null); const form = reactive(emptyForm()); const status = ref(""); const kind = ref<"info" | "success" | "error">("info"); const search = ref("");
const editingLineIndex = ref(0);
const activeSection = ref<"party" | "positions">("party");
const readOnly = computed(() => ["paid", "cancelled"].includes(form.status));
const readOnlyText = computed(() => form.status === "paid" ? "Bezahlt" : form.status === "cancelled" ? "Storniert" : "");
const supplierName = (supplierId: string) => suppliers.value.find((supplier) => supplier.id === supplierId)?.name ?? supplierId;
const filteredInvoices = computed(() => {
  const needle = search.value.trim().toLowerCase();
  if (!needle) return invoices.value;
  return invoices.value.filter((invoice) =>
    `${invoice.invoice_number} ${supplierName(invoice.supplier_id)} ${invoice.status}`.toLowerCase().includes(needle)
  );
});
async function load() { const [ir, sr, itemr] = await Promise.all([apiGet<IncomingInvoice[]>("/api/v1/incoming-invoices"), apiGet<Supplier[]>("/api/v1/suppliers"), apiGet<Item[]>("/api/v1/items")]); if (ir.ok) invoices.value = ir.data; else { status.value = ir.message; kind.value = "error"; } if (sr.ok) suppliers.value = sr.data.filter((supplier) => supplier.status === "active"); if (itemr.ok) items.value = itemr.data.filter((item) => item.status === "active"); }
async function createNew() { selectedId.value = null; Object.assign(form, emptyForm()); editingLineIndex.value = 0; activeSection.value = "party"; form.invoice_number = (await reserveNextNumber("incoming_invoices")) ?? ""; }
function select(invoice: IncomingInvoice) { selectedId.value = invoice.id; Object.assign(form, { ...invoice, items: invoice.items.map((item) => ({ ...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 itemName(itemId: string | null | undefined) {
  if (!itemId) return "Kein Artikel gewählt";
  const item = items.value.find((record) => record.id === itemId);
  return item ? `${item.item_number} - ${item.name}` : itemId;
}
function itemUnit(itemId: string | null | undefined) {
  return items.value.find((record) => record.id === itemId)?.unit ?? "";
}
function quantityWithUnit(line: IncomingInvoiceItem) {
  const unit = itemUnit(line.item_id);
  return unit ? `${line.quantity} ${unit}` : line.quantity;
}
function optionalId(value: string | null | undefined) {
  return value && value.trim() ? value : null;
}
function shouldSendLine(line: IncomingInvoiceItem) {
  if (optionalId(line.item_id)) return true;
  return Boolean(line.description.trim()) || decimalString(line.unit_price) !== "0";
}
function lineIdentity(line: IncomingInvoiceItem) {
  return [
    `item:${optionalId(line.item_id) ?? ""}`,
    line.description.trim(),
    decimalString(line.unit_price),
    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()) editingLineIndex.value = -1; }
function isNewLine(line: IncomingInvoiceItem) { return !line.id; }
function activateSection(section: "party" | "positions") { activeSection.value = section; }
function lineTotal(line: IncomingInvoiceItem) {
  const total = lineNetTotal(line);
  return Number.isFinite(total) ? total.toLocaleString("de-DE", { minimumFractionDigits: 2, maximumFractionDigits: 4 }) : "-";
}
function lineNetTotal(line: IncomingInvoiceItem) {
  const quantity = Number(decimalString(line.quantity, "0"));
  const unitPrice = Number(decimalString(line.unit_price, "0"));
  return quantity * unitPrice;
}
function lineTaxTotal(line: IncomingInvoiceItem) {
  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,
    items: form.items
      .filter(shouldSendLine)
      .map((item) => ({
        ...item,
        item_id: optionalId(item.item_id),
        quantity: decimalString(item.quantity, "1"),
        unit_price: decimalString(item.unit_price),
        tax_rate: normalizeTaxRate(item.tax_rate)
      }))
  };
  const result = selectedId.value ? await apiPut<IncomingInvoice>(`/api/v1/incoming-invoices/${selectedId.value}`, payload) : await apiPost<IncomingInvoice>("/api/v1/incoming-invoices", payload);
  status.value = result.ok ? "Eingangsrechnung gespeichert." : result.message; kind.value = result.ok ? "success" : "error"; if (result.ok) { selectedId.value = result.data.id; await load(); }
}
async function cancelInvoice() { if (readOnly.value || !selectedId.value) return; const result = await apiDelete(`/api/v1/incoming-invoices/${selectedId.value}`); status.value = result.ok ? "Eingangsrechnung storniert." : result.message; kind.value = result.ok ? "success" : "error"; if (result.ok) await load(); }
onMounted(load); watch(() => liveUpdateState.revision, load);
</script>
<template>
  <PageHeader title="Eingangsrechnungen" description="Lieferantenrechnungen mit Skonto-Bezug und Positionen." />
  <div class="workspace-split"><section class="panel list-panel"><div class="section-title"><h2>Eingangsrechnungen</h2><button type="button" class="add-button" title="Neue Eingangsrechnung" @click="createNew">+</button></div><label class="field list-search"><span>Suche</span><input v-model="search" type="search" placeholder="Rechnungsnummer, Lieferant oder Status" /></label><button v-for="invoice in filteredInvoices" :key="invoice.id" type="button" class="list-row" :class="{ selected: selectedId === invoice.id }" @click="select(invoice)"><strong>{{ invoice.invoice_number }}</strong><span>{{ supplierName(invoice.supplier_id) }}</span><small>{{ invoice.status }}</small></button><p v-if="invoices.length === 0" class="empty">Keine Eingangsrechnungen vorhanden.</p><p v-else-if="filteredInvoices.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">Diese Eingangsrechnung ist schreibgeschützt: {{ readOnlyText }}.</div>
        <div class="accordion-section" :class="{ active: activeSection === 'party' }">
          <button type="button" class="accordion-header" @click="activateSection('party')"><span>Lieferant</span><strong>{{ activeSection === 'party' ? "▾" : "▸" }}</strong></button>
          <div v-if="activeSection === 'party'" class="accordion-body form-grid">
            <label class="field"><span>Rechnungsnummer</span><div class="readonly-value">{{ form.invoice_number || "wird automatisch vergeben" }}</div></label>
            <label class="field"><span>Lieferant</span><div v-if="readOnly" class="readonly-value">{{ supplierName(form.supplier_id) }}</div><SearchSelect v-else v-model="form.supplier_id" :options="suppliers.map((supplier) => ({ id: supplier.id, number: supplier.supplier_number, name: supplier.name }))" placeholder="Lieferanten-Nr. oder Name suchen" required /></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="received">Erhalten</option><option value="approved">Freigegeben</option><option value="overdue">Überfällig</option></select></label>
            <label class="field"><span>Datum</span><input v-model="form.invoice_date" type="date" :disabled="readOnly" /></label>
            <label class="field"><span>Fällig</span><input v-model="form.due_at" type="date" :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 compact"><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 compact"><strong>{{ itemName(line.item_id) }}</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</span><div v-if="readOnly" class="readonly-value">{{ itemName(line.item_id) }}</div><SearchSelect v-else v-model="line.item_id" :options="items.map((item) => ({ id: item.id, number: item.item_number, name: item.name }))" placeholder="Artikel-Nr. oder Name suchen" allow-empty empty-label="Kein Artikel" /></label><label class="field"><span>Menge</span><input v-model="line.quantity" inputmode="decimal" :disabled="readOnly" /></label><label class="field"><span>Einheit</span><div class="readonly-value">{{ itemUnit(line.item_id) || "-" }}</div></label><label class="field"><span>Preis</span><input v-model="line.unit_price" inputmode="decimal" :disabled="readOnly" /></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="selectedId" type="button" class="secondary" @click="cancelInvoice">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>
