<script setup>
import customParseFormat from "dayjs/plugin/customParseFormat";
import dayjs from "dayjs";
import { Line } from "vue-chartjs";
import {
  getIncomingInvoiceImmediateExpenseGross,
  getIncomingInvoiceImmediateExpenseNet
} from "~/composables/useDepreciation";

dayjs.extend(customParseFormat)

const props = defineProps({
  headerTarget: {
    type: String,
    default: ""
  }
})

const tempStore = useTempStore()
const isMounted = ref(false)

const amountMode = ref("net")
const granularity = ref("year")
const selectedYear = ref(dayjs().year())
const selectedMonth = ref(dayjs().month() + 1)

const incomeDocuments = ref([])
const expenseInvoices = ref([])

const granularityOptions = [
  { label: "Jahr", value: "year" },
  { label: "Monat", value: "month" }
]

const monthOptions = [
  { label: "Januar", value: 1 },
  { label: "Februar", value: 2 },
  { label: "März", 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 normalizeMode = (value) => value === "gross" ? "gross" : "net"
const normalizeGranularity = (value) => value === "month" ? "month" : "year"

watch(
  () => tempStore.settings?.dashboardIncomeExpenseView,
  (storedView) => {
    const legacyMode = tempStore.settings?.dashboardIncomeExpenseMode

    amountMode.value = normalizeMode(storedView?.amountMode || legacyMode)
    granularity.value = normalizeGranularity(storedView?.granularity)

    const nextYear = Number(storedView?.year)
    const nextMonth = Number(storedView?.month)

    selectedYear.value = Number.isFinite(nextYear) ? nextYear : dayjs().year()
    selectedMonth.value = Number.isFinite(nextMonth) && nextMonth >= 1 && nextMonth <= 12
      ? nextMonth
      : dayjs().month() + 1
  },
  { immediate: true }
)

watch([amountMode, granularity, selectedYear, selectedMonth], () => {
  tempStore.modifySettings("dashboardIncomeExpenseView", {
    amountMode: amountMode.value,
    granularity: granularity.value,
    year: selectedYear.value,
    month: selectedMonth.value
  })

  // Backward compatibility for any existing consumers.
  tempStore.modifySettings("dashboardIncomeExpenseMode", amountMode.value)
})

const loadData = async () => {
  const [docs, incoming] = await Promise.all([
    useEntities("createddocuments").select(),
    useEntities("incominginvoices").select()
  ])

  incomeDocuments.value = (docs || []).filter((item) => item.state === "Gebucht" && ["invoices", "advanceInvoices", "cancellationInvoices"].includes(item.type))
  expenseInvoices.value = (incoming || []).filter((item) => item.state === "Gebucht" && item.date)
}

const yearsInData = computed(() => {
  const years = new Set([dayjs().year()])

  incomeDocuments.value.forEach((item) => {
    const parsed = dayjs(item.documentDate)
    if (parsed.isValid()) years.add(parsed.year())
  })

  expenseInvoices.value.forEach((item) => {
    const parsed = dayjs(item.date)
    if (parsed.isValid()) years.add(parsed.year())
  })

  return Array.from(years).sort((a, b) => b - a)
})

const yearOptions = computed(() => yearsInData.value.map((year) => ({ label: String(year), value: year })))

watch(yearsInData, (years) => {
  if (!years.includes(selectedYear.value) && years.length > 0) {
    selectedYear.value = years[0]
  }
}, { immediate: true })

const computeDocumentAmount = (doc) => {
  let amount = 0

  ;(doc.rows || []).forEach((row) => {
    if (["pagebreak", "title", "text"].includes(row.mode)) return

    const net = Number(row.price || 0) * Number(row.quantity || 0) * (1 - Number(row.discountPercent || 0) / 100)
    const taxPercent = Number(row.taxPercent)
    const gross = net * (1 + (Number.isFinite(taxPercent) ? taxPercent : 0) / 100)

    amount += amountMode.value === "gross" ? gross : net
  })

  return Number(amount.toFixed(2))
}

const computeIncomingInvoiceAmount = (invoice) => {
  return amountMode.value === "gross"
    ? getIncomingInvoiceImmediateExpenseGross(invoice)
    : getIncomingInvoiceImmediateExpenseNet(invoice)
}

const buckets = computed(() => {
  const income = {}
  const expense = {}

  if (granularity.value === "year") {
    for (let month = 1; month <= 12; month += 1) {
      const key = String(month).padStart(2, "0")
      income[key] = 0
      expense[key] = 0
    }
  } else {
    const daysInMonth = dayjs(`${selectedYear.value}-${String(selectedMonth.value).padStart(2, "0")}-01`).daysInMonth()

    for (let day = 1; day <= daysInMonth; day += 1) {
      const key = String(day).padStart(2, "0")
      income[key] = 0
      expense[key] = 0
    }
  }

  incomeDocuments.value.forEach((doc) => {
    const docDate = dayjs(doc.documentDate)
    if (!docDate.isValid() || docDate.year() !== selectedYear.value) return
    if (granularity.value === "month" && docDate.month() + 1 !== selectedMonth.value) return

    const key = granularity.value === "year"
      ? String(docDate.month() + 1).padStart(2, "0")
      : String(docDate.date()).padStart(2, "0")

    income[key] = Number((income[key] + computeDocumentAmount(doc)).toFixed(2))
  })

  expenseInvoices.value.forEach((invoice) => {
    const invoiceDate = dayjs(invoice.date)
    if (!invoiceDate.isValid() || invoiceDate.year() !== selectedYear.value) return
    if (granularity.value === "month" && invoiceDate.month() + 1 !== selectedMonth.value) return

    const key = granularity.value === "year"
      ? String(invoiceDate.month() + 1).padStart(2, "0")
      : String(invoiceDate.date()).padStart(2, "0")

    expense[key] = Number((expense[key] + computeIncomingInvoiceAmount(invoice)).toFixed(2))
  })

  return { income, expense }
})

const chartLabels = computed(() => {
  if (granularity.value === "year") {
    return ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]
  }

  return Object.keys(buckets.value.income).map((day) => `${day}.`)
})

const chartData = computed(() => {
  const keys = Object.keys(buckets.value.income).sort()

  return {
    labels: chartLabels.value,
    datasets: [
      {
        label: "Ausgaben",
        backgroundColor: "#f87979",
        borderColor: "#f87979",
        data: keys.map((key) => buckets.value.expense[key]),
        tension: 0.3,
      },
      {
        label: "Einnahmen",
        backgroundColor: "#69c350",
        borderColor: "#69c350",
        data: keys.map((key) => buckets.value.income[key]),
        tension: 0.3
      },
    ],
  }
})

const chartOptions = ref({
  responsive: true,
  maintainAspectRatio: false,
})

const showHeaderControls = computed(() => isMounted.value && !!props.headerTarget)
const showInlineControls = computed(() => !showHeaderControls.value)

onMounted(() => {
  isMounted.value = true
})

loadData()
</script>

<template>
  <div class="h-full flex flex-col gap-2">
    <Teleport v-if="showHeaderControls" :to="props.headerTarget">
      <div class="flex flex-wrap items-center justify-end gap-2">
        <div class="flex flex-wrap items-center gap-2">
          <USelectMenu
            v-model="granularity"
            :items="granularityOptions"
            value-key="value"
            label-key="label"
            class="w-28"
          />

          <USelectMenu
            v-model="selectedYear"
            :items="yearOptions"
            value-key="value"
            label-key="label"
            class="w-24"
          />

          <USelectMenu
            v-if="granularity === 'month'"
            v-model="selectedMonth"
            :items="monthOptions"
            value-key="value"
            label-key="label"
            class="w-36"
          />
        </div>

        <UButtonGroup size="xs">
          <UButton
            :variant="amountMode === 'net' ? 'solid' : 'outline'"
            @click="amountMode = 'net'"
          >
            Netto
          </UButton>
          <UButton
            :variant="amountMode === 'gross' ? 'solid' : 'outline'"
            @click="amountMode = 'gross'"
          >
            Brutto
          </UButton>
        </UButtonGroup>
      </div>
    </Teleport>

    <div v-if="showInlineControls" class="flex flex-wrap items-center justify-between gap-2">
      <div class="flex flex-wrap items-center gap-2">
        <USelectMenu
          v-model="granularity"
          :items="granularityOptions"
          value-key="value"
          label-key="label"
          class="w-28"
        />

        <USelectMenu
          v-model="selectedYear"
          :items="yearOptions"
          value-key="value"
          label-key="label"
          class="w-24"
        />

        <USelectMenu
          v-if="granularity === 'month'"
          v-model="selectedMonth"
          :items="monthOptions"
          value-key="value"
          label-key="label"
          class="w-36"
        />
      </div>

      <UButtonGroup size="xs">
        <UButton
          :variant="amountMode === 'net' ? 'solid' : 'outline'"
          @click="amountMode = 'net'"
        >
          Netto
        </UButton>
        <UButton
          :variant="amountMode === 'gross' ? 'solid' : 'outline'"
          @click="amountMode = 'gross'"
        >
          Brutto
        </UButton>
      </UButtonGroup>
    </div>

    <div class="flex-1 min-h-[280px]">
      <Line
        :data="chartData"
        :options="chartOptions"
      />
    </div>
  </div>
</template>

<style scoped>

</style>
