<script setup>
import "ag-grid-community/styles/ag-grid.css" // Core CSS
//import "ag-grid-community/styles/ag-theme-quartz.css" // Theme
//import "ag-grid-community/styles/ag-theme-material.css" // Theme
import "ag-grid-community/styles/ag-theme-balham.css" // Theme

import { AgGridVue } from "ag-grid-vue3" // Vue Grid Logic
import {
  ModuleRegistry,
  DateFilterModule,
  NumberFilterModule,
  TextFilterModule,
  provideGlobalGridOptions
} from "ag-grid-community"
import {
  AllEnterpriseModule,
  LicenseManager,
  ColumnMenuModule,
  ColumnsToolPanelModule,
  ContextMenuModule,
  FiltersToolPanelModule,
  SetFilterModule
} from 'ag-grid-enterprise'

const config = useRuntimeConfig()

ModuleRegistry.registerModules([AllEnterpriseModule]);
provideGlobalGridOptions({ theme: "legacy" })

LicenseManager.setLicenseKey(config.public.aggrid ?? "Using_this_{AG_Grid}_Enterprise_key_{AG-086756}_in_excess_of_the_licence_granted_is_not_permitted___Please_report_misuse_to_legal@ag-grid.com___For_help_with_changing_this_key_please_contact_info@ag-grid.com___{LogYou_GmbH}_is_granted_a_{Single_Application}_Developer_License_for_the_application_{logyou}_only_for_{1}_Front-End_JavaScript_developer___All_Front-End_JavaScript_developers_working_on_{logyou}_need_to_be_licensed___{logyou}_has_not_been_granted_a_Deployment_License_Add-on___This_key_works_with_{AG_Grid}_Enterprise_versions_released_before_{22_April_2026}____[v3]_[01]_MTc3NjgxMjQwMDAwMA==8e4702249545b99c7fe255c8ad2aff09");

const props = defineProps({
  columns: Array, // Column Definitions: Defines & controls grid columns.
  masterDetail: Boolean,
  masterDetailRenderer: Object,
  masterDetailParams: Object,
  columnMenu: [String, Object],
  sourceEndpoint: String,
  rowClassRules: Object,
  filterBySwitch: Object,
  getRowId: Function,
  showExport: { type: Boolean, default: true },
  pdfMeta: { type: Object, default: null },
  pdfExcludeFields: { type: Array, default: () => [] },
  autoSizeStrategy: { type: Object, default: () => ({ type: 'fitCellContents' }) },
})

const emit = defineEmits(['cellClick', 'cellChange', 'cellClicked', 'gridReady', 'rowCount', 'aggregates'])

// Org-bpartner isDisallowExport flag: hides the export toolbar, blocks the
// exposed export functions (pages call them via ref) and suppresses AG Grid's
// built-in context-menu CSV/Excel export.
const { allowExport, load: loadExportFlag } = useDisallowExport()
onMounted(() => { loadExportFlag() })

const gridApi = ref();
const defaultColDef = ref({
  filter: false,
  editable: true
})
const columnTypes = ref({
  nonEditableColumn: { editable: false },
  editableColumn: { editable: true }
})


const rowData = ref(null)

const filterBySwitcher = ref(props.filterBySwitch || {})

const serverSideDatasource = () => {
  return {
    getRows(params) {
      const currentFilter = filterBySwitcher.value || {}

      fetch('/api/'+props.sourceEndpoint, {
        method: 'post',
        body: JSON.stringify({...params.request, ...currentFilter}),
        headers: {"Content-Type": "application/json; charset=utf-8"}
      })
      .then(httpResponse => httpResponse.json())
      .then(response => {
          params.success({ rowData: response.rows, rowCount: response.lastRow })
          emit('rowCount', response.lastRow)
          if (response.totalFees !== undefined || response.totalGrandTotal !== undefined) {
            emit('aggregates', { totalFees: response.totalFees, totalGrandTotal: response.totalGrandTotal })
          }
      })
      .catch(error => {
          console.log(error)
          params.fail()
      })
    }
  }
}

const onGridReady = (params) => {
  gridApi.value = params.api

  gridApi.value.setGridOption('serverSideDatasource', serverSideDatasource())
  emit('gridReady', params)
}

const exporting = ref(false)

const getExportColumns = () => {
  const displayedCols = gridApi.value?.getAllDisplayedColumns() || []
  return displayedCols
    .map(col => ({
      field: col.getColDef().field,
      headerName: col.getColDef().headerName || col.getColDef().field || '',
    }))
    .filter(c => c.field && !props.pdfExcludeFields.includes(c.field))
}

const fetchAllRows = async () => {
  const filterModel = gridApi.value?.getFilterModel() || {}
  const sortModel = gridApi.value?.getColumnState()
    ?.filter(c => c.sort)
    .map(c => ({ colId: c.colId, sort: c.sort })) || []
  const currentFilter = filterBySwitcher.value || {}

  const res = await fetch('/api/' + props.sourceEndpoint, {
    method: 'post',
    body: JSON.stringify({ startRow: 0, endRow: 999999, filterModel, sortModel, ...currentFilter }),
    headers: { 'Content-Type': 'application/json; charset=utf-8' },
  })
  const data = await res.json()
  return data.rows || []
}

const getCellValue = (row, field) => {
  const parts = field.split('.')
  let val = row
  for (const p of parts) {
    if (val == null) return ''
    val = val[p]
  }
  if (val != null && typeof val === 'object') {
    return val.identifier ?? val.name ?? JSON.stringify(val)
  }
  return val ?? ''
}

const exportCsv = async () => {
  if (exporting.value || !allowExport.value) return
  exporting.value = true
  try {
    const columns = getExportColumns()
    const rows = await fetchAllRows()

    const escape = (v) => {
      const s = String(v)
      return '"' + s.replace(/"/g, '""') + '"'
    }

    const header = columns.map(c => escape(c.headerName)).join(';')
    const lines = rows.map(row =>
      columns.map(c => escape(getCellValue(row, c.field))).join(';')
    )
    const csv = [header, ...lines].join('\n')

    const blob = new Blob(['\uFEFF' + csv], { type: 'text/csv;charset=utf-8;' })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = (props.sourceEndpoint?.replace(/\//g, '-') || 'export') + '.csv'
    a.click()
    URL.revokeObjectURL(url)
  } catch (e) {
    console.error('CSV export failed:', e)
  } finally {
    exporting.value = false
  }
}

const exportExcel = async () => {
  if (exporting.value || !allowExport.value) return
  exporting.value = true
  try {
    const columns = getExportColumns()
    const rows = await fetchAllRows()
    const XLSX = await import('xlsx')

    const headerRow = columns.map(c => c.headerName)
    const dataRows = rows.map(row =>
      columns.map(c => {
        const val = getCellValue(row, c.field)
        const num = Number(val)
        if (val !== '' && !isNaN(num) && typeof val !== 'boolean') return num
        return val
      })
    )

    const ws = XLSX.utils.aoa_to_sheet([headerRow, ...dataRows])
    const wb = XLSX.utils.book_new()
    XLSX.utils.book_append_sheet(wb, ws, 'Export')
    XLSX.writeFile(wb, (props.sourceEndpoint?.replace(/\//g, '-') || 'export') + '.xlsx')
  } catch (e) {
    console.error('Excel export failed:', e)
  } finally {
    exporting.value = false
  }
}

const formatCurrency = (amount) => {
  return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(amount)
}

const exportPdf = async () => {
  if (exporting.value || !allowExport.value) return
  exporting.value = true
  try {
    const columns = getExportColumns()
    const rows = await fetchAllRows()
    const meta = props.pdfMeta || {}

    const pdfMakeModule = await import('pdfmake/build/pdfmake.js')
    const pdfFontsModule = await import('pdfmake/build/vfs_fonts.js')
    const pdfMake = pdfMakeModule.default || pdfMakeModule

    let vfs = null
    if (pdfFontsModule.pdfMake?.vfs) vfs = pdfFontsModule.pdfMake.vfs
    else if (pdfFontsModule.default?.pdfMake?.vfs) vfs = pdfFontsModule.default.pdfMake.vfs
    else if (pdfFontsModule.default?.vfs) vfs = pdfFontsModule.default.vfs
    else if (pdfFontsModule.default) {
      const keys = Object.keys(pdfFontsModule.default)
      if (keys.some(k => k.endsWith('.ttf'))) vfs = pdfFontsModule.default
    }
    pdfMake.vfs = vfs

    // Landscape A4 usable width: 841.89 - 20 - 20 = ~800pt
    const pageContentWidth = 800

    // Build data table with columns sized to fit
    const tableBody = [
      columns.map(c => ({ text: c.headerName, style: 'tableHeader' })),
      ...rows.map(row =>
        columns.map(c => ({ text: String(getCellValue(row, c.field)), style: 'tableCell' }))
      ),
    ]
    const colWidths = columns.map(() => pageContentWidth / columns.length)

    const content = []

    // ── Header ──
    content.push({
      columns: [
        { text: meta.title || 'Export Report', style: 'header', width: '*' },
        {
          stack: [
            { text: meta.subtitle || '', style: 'subheader', alignment: 'right' },
            { text: new Date().toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }), style: 'subheaderDate', alignment: 'right' },
          ],
          width: 'auto',
        },
      ],
      margin: [0, 0, 0, 8],
    })

    // Blue accent line
    content.push({
      canvas: [{ type: 'line', x1: 0, y1: 0, x2: pageContentWidth, y2: 0, lineWidth: 2.5, lineColor: '#3b82f6' }],
      margin: [0, 0, 0, 12],
    })

    // ── Summary cards row ──
    const hasSummary = meta.totalRows !== undefined || meta.totalFees !== undefined || meta.totalGrandTotal !== undefined
    if (hasSummary) {
      const cardColumns = []

      if (meta.totalRows !== undefined) {
        cardColumns.push({
          width: '*',
          table: {
            widths: ['*'],
            body: [[{
              stack: [
                { text: meta.totalRowsLabel || 'Total Records', fontSize: 8, color: '#6b7280', margin: [0, 0, 0, 2] },
                { text: meta.totalRows.toLocaleString('de-DE'), fontSize: 16, bold: true, color: '#1e40af' },
              ],
              fillColor: '#eff6ff',
              margin: [10, 8, 10, 8],
            }]],
          },
          layout: { hLineWidth: () => 0, vLineWidth: () => 0 },
        })
      }

      if (meta.totalFees !== undefined) {
        cardColumns.push({
          width: '*',
          table: {
            widths: ['*'],
            body: [[{
              stack: [
                { text: meta.totalFeesLabel || 'Gebühr (Fee)', fontSize: 8, color: '#6b7280', margin: [0, 0, 0, 2] },
                { text: formatCurrency(meta.totalFees), fontSize: 16, bold: true, color: '#d35400' },
              ],
              fillColor: '#fef3e2',
              margin: [10, 8, 10, 8],
            }]],
          },
          layout: { hLineWidth: () => 0, vLineWidth: () => 0 },
        })
      }

      if (meta.totalGrandTotal !== undefined) {
        cardColumns.push({
          width: '*',
          table: {
            widths: ['*'],
            body: [[{
              stack: [
                { text: meta.totalGrandTotalLabel || 'Umsatz (Income)', fontSize: 8, color: '#6b7280', margin: [0, 0, 0, 2] },
                { text: formatCurrency(meta.totalGrandTotal), fontSize: 16, bold: true, color: '#1e8449' },
              ],
              fillColor: '#eafaf1',
              margin: [10, 8, 10, 8],
            }]],
          },
          layout: { hLineWidth: () => 0, vLineWidth: () => 0 },
        })
      }

      content.push({ columns: cardColumns, columnGap: 10, margin: [0, 0, 0, 12] })
    }

    // ── Active filters as pill row ──
    if (meta.filters && meta.filters.length > 0) {
      const filterPills = meta.filters.map(f => ({
        table: {
          body: [[{
            text: `${f.label}: ${f.value}`,
            fontSize: 8,
            color: '#374151',
            margin: [6, 3, 6, 3],
          }]],
        },
        layout: {
          hLineWidth: () => 0.5,
          vLineWidth: () => 0.5,
          hLineColor: () => '#d1d5db',
          vLineColor: () => '#d1d5db',
          fillColor: () => '#f9fafb',
        },
      }))

      content.push({
        columns: [
          { text: 'Filters: ', fontSize: 9, bold: true, color: '#6b7280', width: 'auto', margin: [0, 4, 4, 0] },
          ...filterPills.map(p => ({ ...p, width: 'auto', margin: [0, 0, 4, 0] })),
        ],
        margin: [0, 0, 0, 12],
      })
    }

    // Thin separator before table
    content.push({
      canvas: [{ type: 'line', x1: 0, y1: 0, x2: pageContentWidth, y2: 0, lineWidth: 0.5, lineColor: '#e5e7eb' }],
      margin: [0, 0, 0, 8],
    })

    // ── Data table ──
    content.push({
      table: {
        headerRows: 1,
        widths: colWidths,
        body: tableBody,
        dontBreakRows: true,
      },
      layout: {
        fillColor: (rowIndex) => rowIndex === 0 ? '#f3f4f6' : (rowIndex % 2 === 0 ? '#fafafa' : null),
        hLineWidth: () => 0.5,
        vLineWidth: () => 0.5,
        hLineColor: () => '#e5e7eb',
        vLineColor: () => '#e5e7eb',
        paddingLeft: () => 3,
        paddingRight: () => 3,
        paddingTop: () => 2,
        paddingBottom: () => 2,
      },
    })

    const docDefinition = {
      pageOrientation: 'landscape',
      pageSize: 'A4',
      pageMargins: [20, 25, 20, 40],
      footer: (currentPage, pageCount) => ({
        columns: [
          { text: meta.footerText || '', style: 'disclaimer', width: '*' },
          { text: `${currentPage} / ${pageCount}`, style: 'pageNumber', width: 'auto', alignment: 'right' },
        ],
        margin: [20, 8, 20, 15],
      }),
      content,
      styles: {
        header: { fontSize: 18, bold: true, color: '#1e40af' },
        subheader: { fontSize: 10, color: '#4b5563' },
        subheaderDate: { fontSize: 10, color: '#9ca3af', margin: [0, 2, 0, 0] },
        tableHeader: { fontSize: 7, bold: true, color: '#374151' },
        tableCell: { fontSize: 6.5, color: '#4b5563' },
        disclaimer: { fontSize: 7, italics: true, color: '#9ca3af' },
        pageNumber: { fontSize: 7, color: '#9ca3af' },
      },
      defaultStyle: { font: 'Roboto' },
    }

    pdfMake.createPdf(docDefinition).download(
      (props.sourceEndpoint?.replace(/\//g, '-') || 'export') + '.pdf'
    )
  } catch (e) {
    console.error('PDF export failed:', e)
  } finally {
    exporting.value = false
  }
}

defineExpose({
  refreshGrid: (purge = true) => gridApi.value?.refreshServerSide({ route: undefined, purge }),
  applyServerSideTransaction: (params) => gridApi.value?.applyServerSideTransaction(params),
  getEditingCells: () => gridApi.value?.getEditingCells(),
  refreshCells: (params) => gridApi.value?.refreshCells(params),
  getGridApi: () => gridApi.value,
  fetchAllRows,
  exportCsv,
  exportPdf,
  exportExcel,
})

const onCellValueChanged = (event) => {
  emit('cellChange', event)
}

const onSelectionChanged = (event) => {
  emit('cellClick', event, gridApi.value)
}

const onCellClicked = (event) => {
  emit('cellClicked', event)
}

// A changed filter changes what a selection means — drop it so rows that are no longer
// visible can't silently stay in the page's selection scope (batch update / delete).
const onFilterChanged = () => {
  gridApi.value?.deselectAll()
}

watch(() => props.filterBySwitch, (filter) => {
  filterBySwitcher.value = filter || props.filterBySwitch || {}
  if(gridApi.value) {
    gridApi.value.deselectAll()
    gridApi.value.refreshServerSide({ purge: true })
  }
}, { deep: true })
</script>

<template>
  <div style="display: flex; flex-direction: column; height: 100%; min-height: 0;">
    <div v-if="showExport && allowExport" class="export-toolbar">
      <button class="button is-small is-light" :disabled="exporting" title="Export CSV" @click="exportCsv">
        <UIcon name="i-mdi-file-delimited-outline" />
      </button>
      <button class="button is-small is-light" :disabled="exporting" title="Export PDF" @click="exportPdf">
        <UIcon name="i-mdi-file-pdf-box" />
      </button>
      <span v-if="exporting" class="is-size-7 has-text-grey ml-2">Exporting...</span>
    </div>
    <!-- The AG Grid component -->
    <AgGridVue
      :rowData="rowData"
      :autoSizeStrategy="props.autoSizeStrategy"
      rowModelType="serverSide"
      :columnDefs="columns"
      :defaultColDef="defaultColDef"
      :columnTypes="columnTypes"
      :cacheBlockSize="50"
      :maxBlocksInCache="10"
      rowSelection="multiple"
      :columnMenu="columnMenu ?? null"
      :rowClassRules="rowClassRules ?? null"
      :masterDetail="masterDetail ?? false"
      :detailCellRenderer="masterDetailRenderer ?? null"
      :detailCellRendererParams="masterDetailParams ?? null"
      :detailRowAutoHeight="true"
      :getRowId="getRowId ?? undefined"
      :suppressScrollOnNewData="true"
      :suppressCsvExport="!allowExport"
      :suppressExcelExport="!allowExport"
      @grid-ready="onGridReady"
      @cell-value-changed="onCellValueChanged"
      @selection-changed="onSelectionChanged"
      @cell-clicked="onCellClicked"
      @filter-changed="onFilterChanged"
      class="ag-theme-balham"
      style="width: 100%; height: 100%;" />
  </div>
</template>

<style>
@ref w-100pct;

.tf-icon.ti {
  font-size: 22px !important;
  margin-top: -1px;
}

.export-toolbar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem 0;
}
</style>