<script setup lang="ts">
import dayjs from "dayjs"
import {
  getCreatedDocumentTaxBreakdown,
  getIncomingInvoiceTaxBreakdown
} from "~/composables/useTaxEvaluation"
import {
  getIncomingInvoiceDepreciationRows,
  getIncomingInvoiceImmediateExpenseGross,
  getIncomingInvoiceImmediateExpenseNet,
  isDepreciationBookingMode,
  normalizeIncomingInvoiceAccount,
  getStatementAllocationImmediateExpenseAmount
} from "~/composables/useDepreciation"

const router = useRouter()

const loading = ref(true)
const createdDocuments = ref<any[]>([])
const incomingInvoices = ref<any[]>([])
const accounts = ref<any[]>([])
const ownAccounts = ref<any[]>([])
const statementAllocations = ref<any[]>([])

const selectedYear = ref(String(dayjs().year()))
const selectedMonth = ref("all")

const monthItems = [
  { label: "Ganzes Jahr", value: "all" },
  { label: "Januar", value: "1" },
  { label: "Februar", value: "2" },
  { label: "Maerz", value: "3" },
  { label: "April", value: "4" },
  { label: "Mai", value: "5" },
  { label: "Juni", value: "6" },
  { label: "Juli", value: "7" },
  { label: "August", value: "8" },
  { label: "September", value: "9" },
  { label: "Oktober", value: "10" },
  { label: "November", value: "11" },
  { label: "Dezember", value: "12" }
]

const accountColumns = [
  { accessorKey: "gross", header: "Brutto" },
  { accessorKey: "net", header: "Netto" },
  { accessorKey: "tax", header: "Steuer" },
  { accessorKey: "number", header: "Nummer" },
  { accessorKey: "label", header: "Konto" },
  { accessorKey: "bookings", header: "Buchungen" }
]

const ownAccountColumns = [
  { accessorKey: "balance", header: "Saldo" },
  { accessorKey: "expenses", header: "Ausgaben" },
  { accessorKey: "income", header: "Einnahmen" },
  { accessorKey: "number", header: "Nummer" },
  { accessorKey: "label", header: "Konto" },
  { accessorKey: "bookings", header: "Buchungen" }
]

const depreciationColumns = [
  { accessorKey: "label", header: "Abschreibung" },
  { accessorKey: "groupLabel", header: "Gruppe" },
  { accessorKey: "modeLabel", header: "Art" },
  { accessorKey: "amount", header: "Betrag" },
  { accessorKey: "bookings", header: "Positionen" }
]

const isRelevantOutputDocument = (doc: any) => {
  return doc?.state === "Gebucht" && ["invoices", "advanceInvoices", "cancellationInvoices"].includes(doc?.type)
}

const isRelevantInputInvoice = (invoice: any) => {
  return invoice?.state === "Gebucht" && !!invoice?.date
}

const sameId = (left: any, right: any) => String(left ?? "") === String(right ?? "")

const getStatementDate = (allocation: any) => {
  return allocation?.bankstatement?.date || allocation?.bankstatement?.valueDate || allocation?.date || allocation?.created_at || null
}

const matchesSelectedPeriod = (dateValue: any) => {
  const parsed = dayjs(dateValue)

  if (!parsed.isValid()) {
    return false
  }

  if (String(parsed.year()) !== selectedYear.value) {
    return false
  }

  if (selectedMonth.value !== "all" && parsed.month() + 1 !== Number(selectedMonth.value)) {
    return false
  }

  return true
}

const computeDocumentNet = (doc: any) => {
  return Number((doc?.rows || []).reduce((sum: number, row: any) => {
    if (!row || ["pagebreak", "title", "text"].includes(row.mode)) {
      return sum
    }

    const quantity = Number(row.quantity || 0)
    const price = Number(row.price || 0)
    const discountPercent = Number(row.discountPercent || 0)

    return sum + (quantity * price * (1 - discountPercent / 100))
  }, 0).toFixed(2))
}

const yearItems = computed(() => {
  const years = new Set<string>([String(dayjs().year())])

  createdDocuments.value.forEach((doc) => {
    const parsed = dayjs(doc.documentDate)
    if (parsed.isValid()) {
      years.add(String(parsed.year()))
    }
  })

  incomingInvoices.value.forEach((invoice) => {
    const parsed = dayjs(invoice.date)
    if (parsed.isValid()) {
      years.add(String(parsed.year()))
    }
  })

  statementAllocations.value.forEach((allocation) => {
    const parsed = dayjs(getStatementDate(allocation))
    if (parsed.isValid()) {
      years.add(String(parsed.year()))
    }
  })

  return Array.from(years)
    .sort((a, b) => Number(b) - Number(a))
    .map((year) => ({ label: year, value: year }))
})

const filteredDocuments = computed(() => {
  return createdDocuments.value.filter((doc) => matchesSelectedPeriod(doc.documentDate))
})

const filteredIncomingInvoices = computed(() => {
  return incomingInvoices.value.filter((invoice) => matchesSelectedPeriod(invoice.date))
})

const filteredStatementAllocations = computed(() => {
  return statementAllocations.value.filter((allocation) => matchesSelectedPeriod(getStatementDate(allocation)))
})

const filteredAccountStatementAllocations = computed(() => {
  return filteredStatementAllocations.value.filter((allocation) => {
    if (allocation.account === null || allocation.account === undefined) {
      return false
    }

    return getStatementAllocationImmediateExpenseAmount(allocation) > 0
  })
})

const selectedPeriodBounds = computed(() => {
  const start = selectedMonth.value === "all"
    ? dayjs(`${selectedYear.value}-01-01`).startOf("day")
    : dayjs(`${selectedYear.value}-${String(selectedMonth.value).padStart(2, "0")}-01`).startOf("month")

  const end = selectedMonth.value === "all"
    ? dayjs(`${selectedYear.value}-12-31`).endOf("day")
    : start.endOf("month")

  return { start, end }
})

const incomeTotal = computed(() => {
  return Number(filteredDocuments.value.reduce((sum, doc) => sum + computeDocumentNet(doc), 0).toFixed(2))
})

const expenseNetTotal = computed(() => {
  const invoiceExpenses = filteredIncomingInvoices.value.reduce((sum, invoice) => {
    return sum + getIncomingInvoiceImmediateExpenseNet(invoice)
  }, 0)

  const directAccountExpenses = filteredAccountStatementAllocations.value.reduce((sum, allocation) => {
    return sum + Number(getStatementAllocationImmediateExpenseAmount(allocation) || 0)
  }, 0)

  const depreciations = depreciationTotal.value

  return Number((invoiceExpenses + directAccountExpenses + depreciations).toFixed(2))
})

const expenseGrossTotal = computed(() => {
  const invoiceExpenses = filteredIncomingInvoices.value.reduce((sum, invoice) => sum + getIncomingInvoiceImmediateExpenseGross(invoice), 0)

  const directAccountExpenses = filteredAccountStatementAllocations.value.reduce((sum, allocation) => {
    return sum + Number(getStatementAllocationImmediateExpenseAmount(allocation) || 0)
  }, 0)

  const depreciations = depreciationTotal.value

  return Number((invoiceExpenses + directAccountExpenses + depreciations).toFixed(2))
})

const taxSummary = computed(() => {
  const output = filteredDocuments.value.reduce((sum, doc) => {
    const breakdown = getCreatedDocumentTaxBreakdown(doc)
    return {
      net19: sum.net19 + breakdown.net19,
      tax19: sum.tax19 + breakdown.tax19,
      net7: sum.net7 + breakdown.net7,
      tax7: sum.tax7 + breakdown.tax7,
      net0: sum.net0 + breakdown.net0
    }
  }, { net19: 0, tax19: 0, net7: 0, tax7: 0, net0: 0 })

  const input = filteredIncomingInvoices.value.reduce((sum, invoice) => {
    const breakdown = getIncomingInvoiceTaxBreakdown(invoice)
    return {
      net19: sum.net19 + breakdown.net19,
      tax19: sum.tax19 + breakdown.tax19,
      net7: sum.net7 + breakdown.net7,
      tax7: sum.tax7 + breakdown.tax7,
      net0: sum.net0 + breakdown.net0
    }
  }, { net19: 0, tax19: 0, net7: 0, tax7: 0, net0: 0 })

  const outputTax = Number((output.tax19 + output.tax7).toFixed(2))
  const inputTax = Number((input.tax19 + input.tax7).toFixed(2))

  return {
    output,
    input,
    outputTax,
    inputTax,
    balance: Number((outputTax - inputTax).toFixed(2))
  }
})

const operatingResult = computed(() => {
  return Number((incomeTotal.value - expenseNetTotal.value).toFixed(2))
})

const incomeDocumentCount = computed(() => filteredDocuments.value.length)
const expenseDocumentCount = computed(() => {
  const directExpenseCount = filteredAccountStatementAllocations.value.filter((allocation) => getStatementAllocationImmediateExpenseAmount(allocation) > 0).length
  return filteredIncomingInvoices.value.filter((invoice) => getIncomingInvoiceImmediateExpenseNet(invoice) > 0).length + directExpenseCount
})

const depreciationRows = computed(() => {
  const invoiceRows = filteredIncomingInvoices.value.flatMap((invoice) => getIncomingInvoiceDepreciationRows(invoice, selectedPeriodBounds.value.start, selectedPeriodBounds.value.end))

  const grouped = new Map<string, any>()

  ;invoiceRows.forEach((row: any) => {
    const key = row.group || `${row.mode}:${row.label}`
    const current = grouped.get(key) || {
      id: key,
      label: row.group || row.label,
      groupLabel: row.group || "-",
      modeLabel: row.mode === "depreciation_bundle" ? "Sammelposten" : "Einzeln",
      amount: 0,
      bookings: 0
    }

    current.amount += Number(row.amount || 0)
    current.bookings += 1
    grouped.set(key, current)
  })

  return Array.from(grouped.values())
    .map((row) => ({ ...row, amount: Number(row.amount.toFixed(2)) }))
    .sort((left, right) => Number(right.amount) - Number(left.amount))
})

const depreciationTotal = computed(() => {
  return Number(depreciationRows.value.reduce((sum, row) => sum + Number(row.amount || 0), 0).toFixed(2))
})

const accountRows = computed(() => {
  return accounts.value
    .map((account) => {
      const invoiceBookings = filteredIncomingInvoices.value.flatMap((invoice) => {
        return (invoice.accounts || [])
          .filter((invoiceAccount: any) => !isDepreciationBookingMode(normalizeIncomingInvoiceAccount(invoiceAccount, invoice?.date).bookingMode))
          .filter((invoiceAccount: any) => sameId(invoiceAccount.account?.id || invoiceAccount.account, account.id))
          .map((invoiceAccount: any) => ({
            type: "incominginvoice",
            amountNet: Number(invoiceAccount.amountNet || 0),
            amountTax: Number(invoiceAccount.amountTax || 0),
            amountGross: Number.isFinite(Number(invoiceAccount.amountGross))
              ? Number(invoiceAccount.amountGross)
              : Number(invoiceAccount.amountNet || 0) + Number(invoiceAccount.amountTax || 0)
          }))
      })

      const directBookings = filteredAccountStatementAllocations.value
        .filter((allocation) => getStatementAllocationImmediateExpenseAmount(allocation) > 0)
        .filter((allocation) => sameId(allocation.account?.id || allocation.account, account.id))
        .map((allocation) => {
          const amount = Number(allocation.amount || 0)

          return {
            type: "statementallocation",
            amountNet: amount,
            amountTax: 0,
            amountGross: amount
          }
        })

      const bookings = [...invoiceBookings, ...directBookings]

      if (bookings.length === 0) {
        return null
      }

      const net = bookings.reduce((sum, booking: any) => sum + Number(booking.amountNet || 0), 0)
      const tax = bookings.reduce((sum, booking: any) => sum + Number(booking.amountTax || 0), 0)
      const gross = bookings.reduce((sum, booking: any) => {
        const amountGross = Number(booking.amountGross)
        return sum + (Number.isFinite(amountGross) ? amountGross : Number(booking.amountNet || 0) + Number(booking.amountTax || 0))
      }, 0)

      return {
        id: account.id,
        number: account.number || "-",
        label: account.label || account.name || "-",
        bookings: bookings.length,
        net: Number(net.toFixed(2)),
        tax: Number(tax.toFixed(2)),
        gross: Number(gross.toFixed(2))
      }
    })
    .filter(Boolean)
    .sort((left: any, right: any) => Math.abs(Number(right.gross)) - Math.abs(Number(left.gross)))
})

const ownAccountRows = computed(() => {
  return ownAccounts.value
    .map((account) => {
      const bookings = filteredStatementAllocations.value.filter((allocation) => sameId(allocation.ownaccount?.id || allocation.ownaccount, account.id))

      if (bookings.length === 0) {
        return null
      }

      const income = bookings.reduce((sum, booking) => {
        const amount = Number(booking.amount || 0)
        return amount > 0 ? sum + amount : sum
      }, 0)

      const expenses = bookings.reduce((sum, booking) => {
        const amount = Number(booking.amount || 0)
        return amount < 0 ? sum + Math.abs(amount) : sum
      }, 0)

      const balance = bookings.reduce((sum, booking) => sum + Number(booking.amount || 0), 0)

      return {
        id: account.id,
        number: account.number || "-",
        label: account.name || account.label || "-",
        bookings: bookings.length,
        income: Number(income.toFixed(2)),
        expenses: Number(expenses.toFixed(2)),
        balance: Number(balance.toFixed(2))
      }
    })
    .filter(Boolean)
    .sort((left: any, right: any) => Math.abs(Number(right.balance)) - Math.abs(Number(left.balance)))
})

const setupPage = async () => {
  loading.value = true

  try {
    const [docs, invoices, accountItems, ownAccountItems, allocationItems] = await Promise.all([
      useEntities("createddocuments").select(),
      useEntities("incominginvoices").select("*, vendor(*)"),
      useEntities("accounts").selectSpecial(),
      useEntities("ownaccounts").select(),
      useEntities("statementallocations").select("*, bankstatement(*), createddocument(*), incominginvoice(*)")
    ])

    createdDocuments.value = (docs || []).filter(isRelevantOutputDocument)
    incomingInvoices.value = (invoices || []).filter(isRelevantInputInvoice)
    accounts.value = accountItems || []
    ownAccounts.value = ownAccountItems || []
    statementAllocations.value = allocationItems || []

    const firstYear = yearItems.value[0]?.value
    if (firstYear && !yearItems.value.some((item) => item.value === selectedYear.value)) {
      selectedYear.value = firstYear
    }
  } finally {
    loading.value = false
  }
}

const openAccount = (rowLike: any) => {
  const row = rowLike?.original || rowLike
  if (row?.id) {
    router.push(`/accounts/show/${row.id}`)
  }
}

const openOwnAccount = (rowLike: any) => {
  const row = rowLike?.original || rowLike
  if (row?.id) {
    router.push(`/standardEntity/ownaccounts/show/${row.id}`)
  }
}

onMounted(setupPage)
</script>

<template>
  <UDashboardNavbar title="BWA">

  </UDashboardNavbar>

  <UDashboardPanelContent class="min-w-0 space-y-6 overflow-x-hidden overflow-y-auto p-4 md:p-6">
    <div class="flex flex-col gap-3 md:flex-row md:items-end">
      <UFormField label="Jahr" class="w-full md:w-48">
        <USelectMenu
            v-model="selectedYear"
            :items="yearItems"
            value-key="value"
            label-key="label"
            class="w-full"
        />
      </UFormField>

      <UFormField label="Monat" class="w-full md:w-56">
        <USelectMenu
            v-model="selectedMonth"
            :items="monthItems"
            value-key="value"
            label-key="label"
            class="w-full"
        />
      </UFormField>
    </div>

      <div class="grid min-w-0 gap-4 md:grid-cols-2 xl:grid-cols-5">
        <UCard class="min-w-0">
          <div class="text-sm text-gray-500 dark:text-gray-400">Einnahmen netto</div>
          <div class="mt-2 text-2xl font-semibold">{{ useCurrency(incomeTotal) }}</div>
          <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
            {{ incomeDocumentCount }} gebuchte Ausgangsbelege
          </div>
        </UCard>

        <UCard class="min-w-0">
          <div class="text-sm text-gray-500 dark:text-gray-400">Ausgaben netto</div>
          <div class="mt-2 text-2xl font-semibold">{{ useCurrency(expenseNetTotal) }}</div>
          <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
            Brutto: {{ useCurrency(expenseGrossTotal) }}
          </div>
        </UCard>

        <UCard class="min-w-0">
          <div class="text-sm text-gray-500 dark:text-gray-400">Abschreibungen</div>
          <div class="mt-2 text-2xl font-semibold text-amber-600 dark:text-amber-400">{{ useCurrency(depreciationTotal) }}</div>
          <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
            {{ depreciationRows.length }} periodisierte Buchungen
          </div>
        </UCard>

        <UCard class="min-w-0">
          <div class="text-sm text-gray-500 dark:text-gray-400">Einnahmen Belege</div>
          <div class="mt-2 text-2xl font-semibold">{{ incomeDocumentCount }}</div>
          <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
            Ausgangsbelege im Zeitraum
          </div>
        </UCard>

        <UCard class="min-w-0">
          <div class="text-sm text-gray-500 dark:text-gray-400">Ausgaben Belege</div>
          <div class="mt-2 text-2xl font-semibold">{{ expenseDocumentCount }}</div>
          <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
            Eingangsbelege plus direkte Buchungen
          </div>
        </UCard>
      </div>

      <div class="grid min-w-0 gap-4 md:grid-cols-2">
        <UCard class="min-w-0">
          <div class="text-sm text-gray-500 dark:text-gray-400">Betriebsergebnis</div>
          <div class="mt-2 text-2xl font-semibold" :class="operatingResult >= 0 ? 'text-primary-500' : 'text-error'">
            {{ useCurrency(operatingResult) }}
          </div>
          <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
            Einnahmen minus Ausgaben netto
          </div>
        </UCard>

        <UCard class="min-w-0">
          <div class="text-sm text-gray-500 dark:text-gray-400">USt-Saldo</div>
          <div class="mt-2 text-2xl font-semibold" :class="taxSummary.balance >= 0 ? 'text-amber-600 dark:text-amber-400' : 'text-primary-500'">
            {{ useCurrency(taxSummary.balance) }}
          </div>
          <div class="mt-2 text-sm text-gray-500 dark:text-gray-400">
            USt {{ useCurrency(taxSummary.outputTax) }} | Vorsteuer {{ useCurrency(taxSummary.inputTax) }}
          </div>
        </UCard>
      </div>

      <div class="grid min-w-0 gap-4 xl:grid-cols-2">
        <UCard class="min-w-0">
          <template #header>
            <div class="font-semibold">USt-Details</div>
          </template>
          
          <div class="space-y-3 text-sm">
            <div class="flex items-center justify-between">
              <span>Netto 19% Ausgangsbelege</span>
              <span>{{ useCurrency(taxSummary.output.net19) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>USt 19%</span>
              <span>{{ useCurrency(taxSummary.output.tax19) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>Netto 7% Ausgangsbelege</span>
              <span>{{ useCurrency(taxSummary.output.net7) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>USt 7%</span>
              <span>{{ useCurrency(taxSummary.output.tax7) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>Steuerfrei</span>
              <span>{{ useCurrency(taxSummary.output.net0 + taxSummary.input.net0) }}</span>
            </div>
          </div>
        </UCard>

        <UCard class="min-w-0">
          <template #header>
            <div class="font-semibold">Vorsteuer-Details</div>
          </template>

          <div class="space-y-3 text-sm">
            <div class="flex items-center justify-between">
              <span>Netto 19% Eingangsbelege</span>
              <span>{{ useCurrency(taxSummary.input.net19) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>Vorsteuer 19%</span>
              <span>{{ useCurrency(taxSummary.input.tax19) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>Netto 7% Eingangsbelege</span>
              <span>{{ useCurrency(taxSummary.input.net7) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>Vorsteuer 7%</span>
              <span>{{ useCurrency(taxSummary.input.tax7) }}</span>
            </div>
            <div class="flex items-center justify-between">
              <span>Steuerfrei</span>
              <span>{{ useCurrency(taxSummary.input.net0) }}</span>
            </div>
          </div>
        </UCard>
      </div>

      <div class="grid min-w-0 gap-4 xl:grid-cols-2">
        <UCard class="min-w-0">
          <template #header>
            <div class="flex items-center justify-between gap-3">
            <span class="font-semibold">Buchungskonten</span>
            <UBadge color="neutral" variant="soft">{{ accountRows.length }}</UBadge>
          </div>
        </template>

        <div class="min-w-0">
          <UTable
              :data="accountRows"
              :columns="normalizeTableColumns(accountColumns)"
              :loading="loading"
              :on-select="openAccount"
              :empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Buchungskonten im ausgewählten Zeitraum' }"
          >
            <template #label-cell="{ row }">
              <div class="truncate font-medium">{{ row.original.label }}</div>
            </template>

            <template #bookings-cell="{ row }">
              <div class="text-right">{{ row.original.bookings }}</div>
            </template>

            <template #net-cell="{ row }">
              <div class="text-right tabular-nums">{{ useCurrency(row.original.net) }}</div>
            </template>

            <template #tax-cell="{ row }">
              <div class="text-right tabular-nums">{{ useCurrency(row.original.tax) }}</div>
            </template>

            <template #gross-cell="{ row }">
              <div class="text-right font-medium tabular-nums">{{ useCurrency(row.original.gross) }}</div>
            </template>
          </UTable>
        </div>
      </UCard>

      <UCard class="min-w-0">
        <template #header>
          <div class="flex items-center justify-between gap-3">
            <span class="font-semibold">Eigene Buchungskonten</span>
            <UBadge color="neutral" variant="soft">{{ ownAccountRows.length }}</UBadge>
          </div>
        </template>

        <div class="min-w-0">
          <UTable
              :data="ownAccountRows"
              :columns="normalizeTableColumns(ownAccountColumns)"
              :loading="loading"
              :on-select="openOwnAccount"
              :empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine eigenen Buchungen im ausgewählten Zeitraum' }"
          >
            <template #label-cell="{ row }">
              <div class="truncate font-medium">{{ row.original.label }}</div>
            </template>

            <template #bookings-cell="{ row }">
              <div class="text-right">{{ row.original.bookings }}</div>
            </template>

            <template #income-cell="{ row }">
              <div class="text-right text-primary-500 tabular-nums">{{ useCurrency(row.original.income) }}</div>
            </template>

            <template #expenses-cell="{ row }">
              <div class="text-right text-error tabular-nums">{{ useCurrency(row.original.expenses) }}</div>
            </template>

            <template #balance-cell="{ row }">
              <div class="text-right font-medium tabular-nums" :class="row.original.balance >= 0 ? 'text-primary-500' : 'text-error'">
                {{ useCurrency(row.original.balance) }}
              </div>
            </template>
          </UTable>
        </div>
        </UCard>
      </div>

      <UCard class="min-w-0" v-if="depreciationRows.length > 0">
        <template #header>
          <div class="flex items-center justify-between gap-3">
            <span class="font-semibold">Abschreibungen</span>
            <UBadge color="warning" variant="soft">{{ depreciationRows.length }}</UBadge>
          </div>
        </template>

        <UTable
            :data="depreciationRows"
            :columns="normalizeTableColumns(depreciationColumns)"
            :loading="loading"
            :empty="{ icon: 'i-heroicons-circle-stack-20-solid', label: 'Keine Abschreibungen im ausgewählten Zeitraum' }"
        >
          <template #amount-cell="{ row }">
            <div class="text-right text-amber-600 dark:text-amber-400 tabular-nums">{{ useCurrency(row.original.amount) }}</div>
          </template>
        </UTable>
      </UCard>
  </UDashboardPanelContent>
</template>
