<template>
  <div class="field is-grouped is-grouped-multiline pl-2" v-if="filters?.length > 0">
    <div class="control mr-5">
      <a class="button is-primary is-small has-text-white has-text-weight-bold"
         @click="updateData({ filters: [] })">Xóa lọc</a>
    </div>
    <div class="control pr-2 mr-5">
      <span class="icon-text">
        <SvgIcon v-bind="{ name: 'sigma.svg', type: 'primary', size: 20 }"></SvgIcon>
        <span class="fsb-18 has-text-primary">{{ totalRows }}</span>
      </span>
    </div>
    <div class="control" v-for="(v, i) in filters" :key="i">
      <div class="tags has-addons is-marginless">
        <a class="tag is-primary has-text-white is-marginless" @click="showCondition(v)">
          {{ v.label?.indexOf('>') >= 0 ? $stripHtml(v.label, 30) : v.label }}
        </a>
        <a class="tag is-delete is-marginless has-text-black-bis" @click="removeFilter(i)"></a>
      </div>
      <span class="help has-text-black-bis">
        {{ v.sort ?? (v.select
          ? '[' + (v.select[0] ? $stripHtml(v.select[0], 20) : '') + '…' + v.select.length + ']'
          : v.condition) }}
      </span>
    </div>
  </div>

  <div class="table-container mb-0 dtp-wrap" ref="container">
    <table class="table is-fullwidth is-bordered is-narrow is-hoverable dtp-table" :style="tableStyle">

      <!-- ═══════════ THEAD ═══════════ -->
      <thead>

        <!-- ROW 1: group headers (chỉ render nếu có ít nhất 1 field có groupHeader) -->
        <tr v-if="hasGroupHeaders" class="dtp-header-group">
          <th
            v-for="(col, i) in groupHeaderRow"
            :key="i"
            :colspan="col.colspan"
            :rowspan="col.rowspan"
            :style="col.style"
            :class="['dtp-th-group', col.isGroup ? 'dtp-th-group--label' : 'dtp-th-group--empty']"
          >
            <div v-if="col.isGroup">{{ col.label }}</div>
            <div v-else :style="col.dropStyle" @click="showField(col.field)">
              <a v-if="col.label?.indexOf('<') < 0">{{ col.label }}</a>
              <a v-else>
                <component :is="dynamicComponent(col.label)" :row="{}"
                           @clickevent="clickEvent($event, {}, col.field)" />
              </a>
            </div>
          </th>
        </tr>

        <!-- ROW 2: chỉ render fields CÓ groupHeader (field không có groupHeader đã rowspan=2 ở Row 1) -->
        <tr class="dtp-header-fields" v-if="hasGroupHeaders">
          <th
            v-for="(field, i) in displayFields.filter(f => f.groupHeader)"
            :key="i"
            :style="field.headerStyle"
            :class="{ 'dtp-th--sorted': isSorted(field) }"
            @click="showField(field)"
          >
            <div :style="field.dropStyle">
              <a v-if="field.label?.indexOf('<') < 0">{{ field.label }}</a>
              <a v-else>
                <component :is="dynamicComponent(field.label)" :row="{}"
                           @clickevent="clickEvent($event, {}, field)" />
              </a>
            </div>
          </th>
        </tr>
        <!-- ROW đơn (không có groupHeader nào): render bình thường -->
        <tr class="dtp-header-fields" v-else>
          <th
            v-for="(field, i) in displayFields"
            :key="i"
            :style="field.headerStyle"
            :class="{ 'dtp-th--sorted': isSorted(field) }"
            @click="showField(field)"
          >
            <div :style="field.dropStyle">
              <a v-if="field.label?.indexOf('<') < 0">{{ field.label }}</a>
              <a v-else>
                <component :is="dynamicComponent(field.label)" :row="{}"
                           @clickevent="clickEvent($event, {}, field)" />
              </a>
            </div>
          </th>
        </tr>

      </thead>

      <!-- ═══════════ TBODY ═══════════ -->
      <tbody>
        <tr v-for="(v, i) in displayData" :key="i">
          <td
            v-for="(field, j) in displayFields"
            :key="j"
            :style="tdStyle(field, v)"
            :class="[
              'dtp-td',
              field.split === 'stack'   ? 'dtp-td--stack'   : '',
              field.split === 'columns' ? 'dtp-td--columns'  : '',
              field.textalign === 'right' ? 'dtp-td--right' : '',
            ]"
            @dblclick="doubleClick(field, v)"
          >
            <!-- ── Custom template ── -->
            <component
              v-if="field.template"
              :is="dynamicComponent(field.template)"
              :row="v"
              @clickevent="clickEvent($event, v, field)"
            />

            <!-- ── split: "stack" ── -->
            <template v-else-if="field.split === 'stack'">
              <div class="dtp-stack">
                <template v-if="v[field.name]?.length > 0">
                  <div
                    class="dtp-stack-row"
                    v-for="(item, si) in v[field.name]"
                    :key="si"
                  >
                    <!-- splitExpression: "{inv_amount} - {plan_amount}" → tính toán số học -->
                    <span v-if="field.splitExpression">
                      <CellValue
                        :value="evalSplitExpression(field.splitExpression, item)"
                        :type="field.splitType || 'number'"
                        :align="field.textalign"
                      />
                    </span>
                    <!-- splitTemplate: "Đợt {cycle} - {code}" → ghép nhiều field -->
                    <span v-else-if="field.splitTemplate" class="dtp-split-tpl">
                      {{ resolveSplitTemplate(field.splitTemplate, item) }}
                    </span>
                    <CellValue
                      v-else
                      :value="item[field.splitField]"
                      :type="field.splitType || detectType(item[field.splitField])"
                      :align="field.textalign"
                    />
                  </div>
                </template>
                <div v-else class="dtp-stack-row dtp-stack-row--empty">
                  <span class="dtp-null">—</span>
                </div>
              </div>
            </template>

            <!-- ── split: "columns" ── -->
            <template v-else-if="field.split === 'columns'">
              <div v-if="!v[field.name] || v[field.name].length === 0"
                   class="dtp-cols-empty">Không có dữ liệu</div>
              <table v-else class="dtp-minitable">
                <thead v-if="field.showSubHeader !== false">
                  <tr>
                    <th v-for="(sf, si) in resolveSubfields(field, v[field.name])" :key="si">
                      {{ sf.label }}
                    </th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="(item, si) in v[field.name]" :key="si">
                    <td
                      v-for="(sf, sj) in resolveSubfields(field, v[field.name])"
                      :key="sj"
                      :class="['dtp-minitd', sf.type === 'currency' || sf.type === 'number' ? 'dtp-minitd--right' : '']"
                    >
                      <CellValue :value="item[sf.name]" :type="sf.type" />
                    </td>
                  </tr>
                </tbody>
                <!-- mini footer sum -->
                <tfoot v-if="field.showSubFooter !== false && hasSummableSubs(field, v[field.name])">
                  <tr>
                    <td
                      v-for="(sf, si) in resolveSubfields(field, v[field.name])"
                      :key="si"
                      :class="sf.type === 'currency' || sf.type === 'number' ? 'dtp-minitd--right' : ''"
                      class="dtp-minifoot"
                    >
                      <template v-if="si === 0">
                        <span class="dtp-null">∑</span>
                      </template>
                      <template v-else-if="sf.type === 'currency' || sf.type === 'number'">
                        <CellValue :value="sumSubField(v[field.name], sf.name)" :type="sf.type" />
                      </template>
                    </td>
                  </tr>
                </tfoot>
              </table>
            </template>

            <!-- ── Normal cell (giữ nguyên logic gốc) ── -->
            <template v-else>
              <CellValue
                v-if="field.type"
                :value="v[field.name]"
                :type="field.type"
                :field="field"
              />
              <span v-else>{{ v[field.name] ?? '' }}</span>
            </template>

          </td>
        </tr>
      </tbody>

      <!-- ═══════════ TFOOT (summary) ═══════════ -->
      <tfoot v-if="showSummary">
        <tr>
          <td
            v-for="(field, i) in displayFields"
            :key="i"
            :style="field.headerStyle"
            class="dtp-foot"
            :class="field.textalign === 'right' ? 'dtp-td--right' : ''"
          >
            <span v-if="i === 0" class="dtp-foot-label">∑</span>
            <CellValue
              v-else-if="(field.summary === 'sum' || field.type === 'currency') && !field.split"
              :value="sumColumn(field)"
              type="currency"
            />
            <span v-else-if="field.summary === 'count'">{{ totalRows }}</span>
          </td>
        </tr>
      </tfoot>

    </table>

    <DatatablePagination
      v-bind="{ data: data, perPage: perPage }"
      @changepage="changePage"
      v-if="showPaging"
    />
  </div>

  <Modal
    @close="close"
    @selected="doSelect"
    @confirm="confirmRemove"
    v-bind="showmodal"
    v-if="showmodal"
  />
</template>

<!-- ════════════════════════ CELL VALUE COMPONENT ════════════════════════ -->
<script>
export const CellValue = {
  name: 'CellValue',
  props: {
    value:  { default: null },
    type:   { type: String, default: 'string' },
    field:  { type: Object, default: () => ({}) },
    align:  { type: String, default: '' },
  },
  setup(props) {
    const { $formatNumber } = useNuxtApp()
    function fmtCurrency(v) {
      if (v === null || v === undefined) return null
      return Number(v).toLocaleString('vi-VN')
    }
    return { fmtCurrency }
  },
  template: `
    <span v-if="value === null || value === undefined" class="dtp-null">—</span>
    <span v-else-if="type === 'currency'"
          :class="['dtp-currency', Number(value) < 0 ? 'dtp-currency--neg' : Number(value) === 0 ? 'dtp-currency--zero' : '']"
    >{{ fmtCurrency(value) }}</span>
    <span v-else-if="type === 'number'"
          class="dtp-number">{{ Number(value).toLocaleString('vi-VN') }}</span>
    <span v-else-if="type === 'date'"
          class="dtp-date">{{ value }}</span>
    <span v-else-if="type === 'code'"
          class="dtp-code">{{ value }}</span>
    <span v-else-if="type === 'badge' && field.badgeMap"
          :class="['dtp-badge', field.badgeColorMap?.[value] ?? '']"
    >{{ field.badgeMap?.[value] ?? value }}</span>
    <span v-else>{{ value }}</span>
  `
}
</script>

<script setup>
import { ref, computed, reactive, watch } from "vue/dist/vue.esm-bundler.js"
import { defineComponent } from "vue"
import { useStore } from "~/stores/index"

const emit = defineEmits(["edit", "insert", "dataevent", "changedata", "changefilter", "displayDataChange"])
const {
  $calc, $calculate, $calculateData, $copy, $deleterow, $empty,
  $find, $getEditRights, $formatNumber, $multiSort, $remove, $stripHtml, $unique,
} = useNuxtApp()
const store = useStore()
const props = defineProps({ pagename: String })

// ── helpers ─────────────────────────────────────────────────────────────────
function dynamicComponent(html) {
  return defineComponent({
    template: html,
    props: { row: { type: Object, default: () => ({}) } },
  })
}

function detectType(value) {
  if (typeof value === 'number') return 'number'
  return 'string'
}

function resolveSubfields(field, rows) {
  if (field.subfields) return field.subfields
  if (!rows?.length) return []
  return Object.keys(rows[0]).map(k => ({
    name: k, label: k,
    type: typeof rows[0][k] === 'number' ? 'number' : 'string',
  }))
}

function hasSummableSubs(field, rows) {
  if (!rows?.length) return false
  return resolveSubfields(field, rows).some(sf => sf.type === 'currency' || sf.type === 'number')
}

function sumSubField(rows, name) {
  return rows.reduce((s, r) => s + (Number(r[name]) || 0), 0)
}

function sumColumn(field) {
  return (data || []).reduce((s, r) => s + (Number(r[field.name]) || 0), 0)
}

function isSorted(field) {
  return filters?.some(f => f.name === field.name && f.sort)
}

function resolveSplitTemplate(template, item) {
  return template.replace(/\{(\w+)\}/g, (_, key) => item[key] ?? '')
}

function evalSplitExpression(expression, item) {
  try {
    const expr = expression.replace(/\{(\w+)\}/g, (_, key) => {
      const v = item[key]
      return (v === null || v === undefined) ? 'null' : Number(v)
    })
    const exprFull = expr.replace(/\b([a-zA-Z_]\w*)\b/g, (match) => {
      if (!(match in item)) return match
      const v = item[match]
      return (v === null || v === undefined) ? 'null' : Number(v)
    })
    return Function('"use strict"; return (' + exprFull + ')')()
  } catch {
    return null
  }
}

// ── state ────────────────────────────────────────────────────────────────────
var timer
var showPaging    = ref(false)
var totalRows     = ref(0)
var currentPage   = 1
var displayFields = ref([])
var displayData   = ref([])
var pagedata      = store[props.pagename]
var tablesetting  = $copy(pagedata.tablesetting || store.tablesetting)
var perPage       = Number($find(tablesetting, { code: 'per-page' }, 'detail')) || 20
var filters       = $copy(pagedata.filters || [])
var currentField
var filterData    = []
var currentRow
var data          = $copy(pagedata.data)
var showmodal     = ref()

const hasGroupHeaders = computed(() =>
  displayFields.value.some(f => f.groupHeader)
)

const groupHeaderRow = computed(() => {
  const fields = displayFields.value
  if (!fields.length) return []

  const result = []
  let i = 0
  while (i < fields.length) {
    const f = fields[i]
    if (!f.groupHeader) {
      result.push({
        label: f.label,
        colspan: 1,
        rowspan: 2,
        isGroup: false,
        style: f.headerStyle || '',
        dropStyle: f.dropStyle || '',
        field: f,
      })
      i++
    } else {
      let count = 1
      while (i + count < fields.length && fields[i + count].groupHeader === f.groupHeader) {
        count++
      }
      result.push({
        label: f.groupHeader,
        colspan: count,
        rowspan: 1,
        isGroup: true,
        style: f.headerStyle || '',
      })
      i += count
    }
  }
  return result
})

// ── summary ──────────────────────────────────────────────────────────────────
const showSummary = computed(() =>
  pagedata?.showSummary !== false &&
  displayFields.value.some(f => f.summary || f.type === 'currency')
)

function tdStyle(field, record) {
  let val = getStyle(field, record)
  if (field.split === 'stack' || field.split === 'columns') {
    val = val.replace(/padding:[^;]+;?/g, '') + 'padding:0;vertical-align:top;'
  }
  return val
}

// ── watch ────────────────────────────────────────────────────────────────────
watch(() => store[props.pagename], () => { updateChange() })

function updateChange() {
  pagedata = store[props.pagename]
  if (!pagedata.update) return
  if (pagedata.update.data) data = $copy(pagedata.update.data)
  if (pagedata.update.filters) { doFilter(pagedata.update.filters); updateShow(); return }
  if (filters.length > 0) doFilter(filters)
  if (pagedata.update.fields || pagedata.update.data) updateShow()
}

const updateShow = function (full_data) {
  const allowedFns = { '$getEditRights()': $getEditRights }
  const arr = pagedata.fields.filter(({ show }) => {
    if (typeof show === 'boolean') return show
    if (show === 'true') return true
    if (show === 'false') return false
    return allowedFns[show]?.() || false
  })

  if (full_data === false) {
    displayData.value = $copy(data)
  } else {
    displayData.value = $copy(
      data.filter((_, idx) =>
        idx >= (currentPage - 1) * perPage && idx < currentPage * perPage
      )
    )
  }

  displayData.value.forEach((v, idx) => {
    v.__stt = (currentPage - 1) * perPage + idx + 1
  })
  arr.forEach(v => {
    v.headerStyle = getSettingStyle('header', v)
    v.dropStyle   = getSettingStyle('dropdown', v)
  })
  displayFields.value = arr
  showPagination()
}

function confirmRemove() {
  $deleterow(pagedata.api.name, currentRow.id, props.pagename, true)
}

const clickEvent = (event, row, field) => {
  const name = typeof event === 'string' ? event : event.name
  const evdata = typeof event === 'string' ? event : event.data
  if (name === 'remove') {
    currentRow = row
    showmodal.value = {
      component: 'dialog/Confirm',
      vbind: { content: 'Bạn có muốn xóa bản ghi này không?', duration: 10 },
      title: 'Xác nhận', width: '500px', height: '100px',
    }
  }
  emit(name, row, field, evdata)
}

const showField = async (field) => {
  if (pagedata.contextMenu === false || field.menu === 'no') return
  currentField = field
  filterData = $unique(pagedata.data, [field.name])
  showmodal.value = {
    vbind: { pagename: props.pagename, field, filters, filterData, width: 100 },
    component: 'datatable/ContextMenu',
    title: field.name, width: '650px', height: '500px',
  }
}

const getStyle = (field, record) => {
  let val = (tablesetting.find(v => v.code === 'td-border')?.detail ?? '') + ';'
  let stop = false
  if (!Array.isArray(field.bgcolor) && field.bgcolor) {
    val += ` background-color:${field.bgcolor};`
  } else if (Array.isArray(field.bgcolor)) {
    for (const v of field.bgcolor) {
      if (stop) break
      const match = v.type === 'search'
        ? record[field.name]?.toLowerCase().includes(v.keyword?.toLowerCase())
        : $calculate(record, v.tags, v.expression)?.value
      if (match) { val += ` background-color:${v.color};`; stop = true }
    }
  }
  stop = false
  if (!Array.isArray(field.color) && field.color) {
    val += ` color:${field.color};`
  } else if (Array.isArray(field.color)) {
    for (const v of field.color) {
      if (stop) break
      if ($calculate(record, v.tags, v.expression)?.value) { val += ` color:${v.color};`; stop = true }
    }
  }
  const fs = tablesetting.find(v => v.code === 'table-font-size')?.detail ?? '12'
  val += ` font-size:${field.textsize ?? fs}px;`
  if (field.textalign) val += ` text-align:${field.textalign};`
  if (field.minwidth)  val += ` min-width:${field.minwidth}px;`
  if (field.maxwidth)  val += ` max-width:${field.maxwidth}px;`
  return val
}

const getSettingStyle = (name, field) => {
  let val = ''
  if (name === 'header') {
    val += `background-color:${tablesetting.find(v => v.code === 'header-background')?.detail};`
    if (field?.minwidth) val += ` min-width:${field.minwidth}px;`
    if (field?.maxwidth) val += ` max-width:${field.maxwidth}px;`
  } else if (name === 'dropdown') {
    val += `font-size:${tablesetting.find(v => v.code === 'header-font-size')?.detail}px;`
    val += filters.find(v => v.name === field?.name)
      ? `color:${tablesetting.find(v => v.code === 'header-filter-color')?.detail};`
      : `color:${tablesetting.find(v => v.code === 'header-font-color')?.detail};`
  }
  return val
}

const changePage = (page) => { currentPage = page; updateShow() }

const showPagination = () => {
  showPaging.value = pagedata.pagination !== false
  totalRows.value = data.length
  if (showPaging.value && pagedata.api) {
    if (pagedata.api.full_data === false) totalRows.value = pagedata.api.total_rows
    showPaging.value = totalRows.value > perPage
  }
}

const close = () => { showmodal.value = undefined }

const frontendFilter = (newVal) => {
  newVal = $copy(newVal)
  let filtered = $copy(pagedata.data)
  newVal.filter(m => m.select || m.filter).forEach(v => {
    if (v.select) {
      filtered = filtered.filter(x =>
        v.select.findIndex(y => ($empty(y) ? $empty(x[v.name]) : y === x[v.name])) > -1
      )
    } else if (v.filter) {
      filtered = filtered.filter(x => {
        if ($empty(x[v.name])) return false
        let text = ''
        v.filter.forEach((y, k) => {
          text += `${k > 0 ? (v.filter[k-1].operator === 'and' ? ' &&' : ' ||') : ''}
            ${$formatNumber(x[v.name])} ${y.condition === '=' ? '==' : y.condition === '<>' ? '!==' : y.condition} ${$formatNumber(y.value)}`
        })
        return $calc(text)
      })
    }
  })
  const sortList = filters.filter(x => x.sort)
  if (!sortList.length) return filtered
  const sort = {}, format = {}
  sortList.forEach(v => { sort[v.name] = v.sort === 'az' ? 'asc' : 'desc'; format[v.name] = v.format })
  return $multiSort(filtered, sort, format)
}

const doFilter = (newVal, nonset) => {
  if (currentPage > 1 && nonset !== true) currentPage = 1
  if (pagedata.api?.full_data) {
    data = frontendFilter(newVal)
    pagedata.dataFilter = $copy(data)
    store.commit(props.pagename, pagedata)
    emit('changedata', newVal)
  }
  pagedata.filters = newVal
  store.commit(props.pagename, pagedata)
  emit('changefilter', newVal?.length > 0)
}

const doSelect = (value) => {
  showmodal.value = undefined
  const field = currentField
  let found = filters.find(v => v.name === field.name)
  if (found) {
    if (!found.select) found.select = []
    const idx = found.select.findIndex(x => x === value)
    idx >= 0 ? $remove(found.select, idx) : found.select.push(value)
    if (found.select.length === 0) {
      const i = filters.findIndex(v => v.name === field.name)
      if (i >= 0) $remove(filters, i)
    }
  } else {
    filters.push({ name: field.name, label: field.label, select: [value], format: field.format })
  }
  doFilter(filters)
  updateShow()
}

const removeFilter = (i) => { $remove(filters, i); doFilter(filters); updateShow() }

const updateData = (newVal) => {
  if (newVal.filters) filters = $copy(newVal.filters)
  else if (pagedata.filters) filters = $copy(pagedata.filters)
  if (newVal.data || newVal.fields || newVal.filters) doFilter(filters)
  if (newVal.data || newVal.fields || newVal.filters || newVal.tablesetting) updateShow()
}

const doubleClick = (field, v) => { currentField = field; doSelect(v[field.name]) }

var tableStyle = getSettingStyle('table')
setTimeout(() => updateShow(), 200)

watch(displayData, (val) => {
  emit('displayDataChange', toRaw(val))
})
</script>

<style scoped>
/* ── Group headers ─────────────────────────────────────────────── */
.dtp-th-group--label {
  text-align: center;
  font-weight: 700;
  letter-spacing: 0.04em;
  color:#ffffff;
  border-bottom: 2px solid rgba(255,255,255,0.3) !important;
}

/* ── Hover (giữ nguyên) ───────────────────────────────────────── */
:deep(.table tbody tr:hover td, .table tbody tr:hover th) {
  background-color: hsl(0, 0%, 78%);
  color: rgb(0, 0, 0);
}

/* ── Stack split ──────────────────────────────────────────────── */
.dtp-td--stack { padding: 0 !important; vertical-align: top; }
.dtp-stack { display: flex; flex-direction: column; }
.dtp-stack-row {
  padding: 4px 8px;
  min-height: 24px;
  display: flex;
  align-items: center;
  border-bottom: 1px solid #e8ecf0;
}
.dtp-stack-row:last-child { border-bottom: none; }
.dtp-stack-row--empty { color: #bbb; font-style: italic; }
.dtp-split-tpl { font-size: 11px; }

/* ── Columns split (mini-table) ───────────────────────────────── */
.dtp-td--columns { padding: 0 !important; vertical-align: top; }
.dtp-cols-empty { padding: 6px 10px; color: #bbb; font-style: italic; }
.dtp-minitable { width: 100%; border-collapse: collapse; }
.dtp-minitable thead th {
  padding: 3px 8px;
  border-right: 1px solid #dde2e8;
  border-bottom: 1px solid #c8cdd3;
  white-space: nowrap;
  text-transform: uppercase;
}
.dtp-minitd {
  padding: 4px 8px;
  border-right: 1px solid #eef1f4;
  border-bottom: 1px solid #eef1f4;
}
.dtp-minitd--right { text-align: right; }
.dtp-minitable tbody tr:last-child .dtp-minitd { border-bottom: none; }
.dtp-minifoot { font-weight: 700; border-top: 1px solid #c8cdd3 !important; padding: 4px 8px; }

/* ── Số âm / zero cho currency ────────────────────────────────── */
.dtp-currency--neg  { color: #ef4444; }
.dtp-currency--zero { color: #b0b7c3; }
</style>