// PivotDataView.vue - FULL CODE (stable mapping, no aggregation, data will show correctly)
<template>
  <div>
    <!-- TimeOption Component -->
    <TimeOption
      v-bind="{ 
        pagename: vpagename, 
        api: props.api, 
        timeopt: props.timeopt, 
        filter: optfilter, 
        importdata: props.importdata, 
        newDataAvailable: newDataAvailable, 
        params: vparams 
      }"
      ref="timeopt" 
      @option="timeOption" 
      @excel="exportExcel" 
      @add="insert" 
      @manual-refresh="manualRefresh" 
      @refresh-data="refreshData"
      @import="openImportModal" 
      class="mb-3" 
      v-if="props.timeopt"
    />
    
    <!-- Filters Display -->
    <div class="field is-grouped is-grouped-multiline pl-2 mb-3" v-if="filters && filters.length > 0">
      <div class="control mr-5">
        <a class="button is-primary is-small has-text-white has-text-weight-bold" @click="clearFilters()">
          <span class="fs-14">Xóa lọc</span>
        </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">{{ pivotData.length }}</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.sort : (v.select ? ('[' + (v.select.length > 0 ? $stripHtml(v.select[0], 20) : '') + '...∑' + v.select.length + ']') : (v.condition)) }}
        </span>
      </div>
    </div>
    
    <!-- Pivot Table -->
    <div class="table-container mb-0" ref="container" v-if="pivotData.length > 0">
      <table class="table is-fullwidth is-bordered is-narrow is-hoverable" :style="tableStyle">
        <thead>
          <tr v-for="(level, levelIndex) in maxHeaderDepth" :key="`header-level-${levelIndex}`">
            <template v-if="levelIndex === 0">
              <th 
                v-for="(rowField, idx) in rowFields" 
                :key="`row-header-group-${idx}`"
                :style="headerStyle" 
                :rowspan="maxHeaderDepth"
                class="is-sticky-left"
                @click="showFieldMenu(rowField)"
              >
                <div :style="rowField.dropStyle">
                  <a v-if="rowField.label.indexOf('<') < 0">{{ rowField.label }}</a>
                  <a v-else>
                    <component :is="dynamicComponent(rowField.label)" :row="{}" @clickevent="clickEvent($event, {}, rowField)" />
                  </a>
                </div>
              </th>
            </template>
            
            <th 
              v-for="(node, nodeIdx) in getNodesAtLevel(levelIndex)" 
              :key="`header-node-${levelIndex}-${nodeIdx}`"
              :colspan="getColspan(node)"
              :rowspan="getRowspan(node, levelIndex)"
              :style="getHeaderStyle(node, levelIndex)"
              style="z-index: 1;"
              class="has-text-centered"
            >
              {{ node.label }}
            </th>
          </tr>
        </thead>
        
        <tbody>
          <tr v-for="(row, rowIndex) in pivotData" :key="`row-${rowIndex}`">
            <td 
              v-for="(rowField, idx) in rowFields"
              :key="`row-cell-${rowIndex}-${idx}`"
              :style="getRowCellStyle(rowField, row)" 
              class="is-sticky-left has-text-weight-bold"
              @dblclick="doubleClick(rowField, row[rowField.name])"
            >
              <component 
                :is="dynamicComponent(rowField.template)" 
                :row="{ [rowField.name]: row[rowField.name] }" 
                v-if="rowField.template"
                @clickevent="clickEvent($event, row, rowField)" 
              />
              <span v-else>{{ formatCellValue(row[rowField.name], rowField) }}</span>
            </td>
            
            <td 
              v-for="(leaf, leafIdx) in allLeafColumns" 
              :key="`data-cell-${rowIndex}-${leafIdx}`"
              :style="getDataCellStyle(row, leaf)"
              @dblclick="handleCellClick(row, leaf)"
              :class="leaf.align || 'has-text-right'"
            >
              <template v-if="row.data[leaf.colKey]">
                <component 
                  :is="dynamicComponent(leaf.template)" 
                  :row="row.data[leaf.colKey]" 
                  v-if="leaf.template"
                  @clickevent="clickEvent($event, row.data[leaf.colKey], leaf)" 
                />
                <span v-else>
                  {{ formatCellValue(row.data[leaf.colKey][leaf.field], leaf) }}
                </span>
              </template>
              <span v-else class="has-text-grey-light">-</span>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    
    <div v-else class="has-text-centered has-text-grey py-6">
      <p>Không có dữ liệu để hiển thị</p>
    </div>
    
    <Modal @close="closeModal" @selected="doSelect" @confirm="confirmRemove" v-bind="showmodal" v-if="showmodal" />
  </div>
</template>

<script setup>
import TimeOption from '~/components/datatable/TimeOption'
import { ref, computed, watch, onMounted, onBeforeUnmount, defineComponent } from 'vue'
import { useStore } from '~/stores/index'

const emit = defineEmits(['modalevent', 'dataevent', 'dataUpdated', 'edit', 'insert'])
const store = useStore()

const props = defineProps({
  api: String,
  pagename: String,
  setting: String,
  params: Object,
  filter: Object,
  pivotConfig: Object,
  timeopt: Object,
  modal: Object,
  realtime: Object,
  importdata: Object,
  data: Object
})

const { 
  $copy, $find, $findapi, $getapi, $setpage, $clone, $stripHtml, 
  $snackbar, $dayjs, $formatNumber, $numtoString, $empty, $unique,
  $multiSort, $remove, $calculate, $deleterow
} = useNuxtApp()

// Dynamic component helper
function dynamicComponent(htmlString) {
  if (!htmlString) return null
  return defineComponent({
    template: htmlString,
    props: {
      row: {
        type: Object,
        default: () => ({})
      }
    }
  })
}

const showmodal = ref()
const pagedata = ref()
const newDataAvailable = ref(false)
const pivotData = ref([])
const columnHeaderTree = ref([])
const filters = ref([])
const currentField = ref()
const currentRow = ref()
const filterData = ref([])
const pollingInterval = ref(null)
const lastDataHash = ref(null)

// Computed properties
const vpagename = props.pagename || 'pagedata_pivot'

const vparams = computed(() => {
  if (!props.params) return undefined
  const params = $copy(props.params)
  if (params.filter) {
    for (const [key, value] of Object.entries(params.filter)) {
      if (typeof value === 'string' && value.indexOf('$') >= 0) {
        const storeKey = value.replace('$', '')
        if (store[storeKey]) {
          params.filter[key] = store[storeKey].id || store[storeKey]
        }
      }
    }
  }
  return params
})

let vfilter = props.filter ? $copy(props.filter) : undefined
let optfilter = props.filter || (props.params ? props.params.filter : undefined)
let connection = undefined
const realtimeConfig = ref({ time: 0, update: "true" })

if (props.realtime) {
  realtimeConfig.value = { time: props.realtime.time || 0, update: props.realtime.update }
}

// Extract pivot config
const rowFields = computed(() => {
  if (!props.pivotConfig?.rowFields) return []
  const fieldNames = props.pivotConfig.rowFields
  return fieldNames.map(name => {
    const field = pagedata.value?.fields?.find(f => f.name === name)
    const result = field || { name, label: name, format: 'string' }
    result.headerStyle = getSettingStyle('header', result)
    result.dropStyle = getSettingStyle('dropdown', result)
    return result
  })
})

const columnTreeConfig = computed(() => {
  const cfg = props.pivotConfig?.columnFields || []
  if (cfg.every(item => typeof item === 'string')) {
    return cfg.map(field => ({ field, label: field }))
  }
  return cfg
})

const subColumns = computed(() => {
  if (!props.pivotConfig?.subColumns || props.pivotConfig.subColumns.length === 0) {
    return [
      { field: 'amount', label: 'Số tiền', format: 'number', decimal: 0, align: 'has-text-right' }
    ]
  }
  return props.pivotConfig.subColumns.map(subCol => {
    const field = pagedata.value?.fields?.find(f => f.name === subCol.field)
    return {
      ...subCol,
      format: subCol.format || field?.format || 'string',
      decimal: subCol.decimal !== undefined ? subCol.decimal : field?.decimal,
      unit: subCol.unit || field?.unit,
      template: subCol.template || field?.template
    }
  })
})

// Build nested column tree
const buildColumnTree = (rawData) => {
  if (!rawData?.length || !columnTreeConfig.value.length) return []

  const rootMap = new Map()

  rawData.forEach(item => {
    let currentMap = rootMap
    let currentPath = ''

    columnTreeConfig.value.forEach((cfg, level) => {
      const field = cfg.field
      const val = item[field] != null ? String(item[field]).trim() : 'N/A'
      const key = val
      const prefix = cfg.prefix || ''
      const nodeLabel = cfg.label ? `${cfg.label} ${val}` : (prefix + val)

      if (!currentMap.has(key)) {
        const node = {
          label: nodeLabel,
          value: val,
          path: currentPath ? currentPath + '|||' + val : val,
          sortType: cfg.sortType || 'string',
          sortOrder: cfg.sortOrder
        }
        if (level < columnTreeConfig.value.length - 1) {
          node.childrenMap = new Map()
        }
        currentMap.set(key, node)
      }

      currentMap = currentMap.get(key)
      currentPath = currentMap.path
      if (level < columnTreeConfig.value.length - 1) {
        currentMap = currentMap.childrenMap
      }
    })

    if (!currentMap.children) {
      currentMap.children = subColumns.value.map(sc => ({
        ...sc,
        colKey: currentPath,
        isLeaf: true
      }))
    }
  })

  const mapToSortedArray = (map) => {
    if (!map || !map.size) return [];
    
    const firstNode = map.values().next().value;
    const sortType = firstNode?.sortType || 'string';
    const sortOrder = firstNode?.sortOrder;

    return Array.from(map.values()).sort((a, b) => {
      const valA = String(a?.value ?? '')
      const valB = String(b?.value ?? '')

      if (sortType === 'number') {
        const numA = Number(valA)
        const numB = Number(valB)
        if (!isNaN(numA) && !isNaN(numB)) return numA - numB
      }

      if (sortType === 'custom' && Array.isArray(sortOrder)) {
        const order = sortOrder;
        const getIndex = (val) => {
            for(let i = 0; i < order.length; i++) {
                if (Array.isArray(order[i]) && order[i].includes(val)) return i;
                if (order[i] === val) return i;
            }
            return -1;
        }
        const indexA = getIndex(valA);
        const indexB = getIndex(valB);

        if (indexA !== -1 || indexB !== -1) {
            if (indexA === -1) return 1;
            if (indexB === -1) return -1;
            if (indexA !== indexB) return indexA - indexB;
        }
      }

      return valA.localeCompare(valB)
    })
  }

  const recurseSort = (node) => {
    if (node.childrenMap) {
      node.children = mapToSortedArray(node.childrenMap)
      delete node.childrenMap
    }
    if (node.children && !node.children[0]?.isLeaf) {
        node.children.forEach(recurseSort)
    }
  }

  const roots = mapToSortedArray(rootMap)
  roots.forEach(recurseSort)
  return roots
}

// Simplified transformToPivot - stable colKey, last wins for duplicates
const transformToPivot = (rawData) => {
  if (!rawData?.length) {
    pivotData.value = []
    columnHeaderTree.value = []
    return
  }

  columnHeaderTree.value = buildColumnTree(rawData)

  const localRowFields = rowFields.value
  const grouped = {}

  rawData.forEach(item => {
    const rowKey = localRowFields.map(f => {
      const val = item[f.name]
      return val != null ? String(val).trim() : 'N/A'
    }).join('|||')

    if (!grouped[rowKey]) {
      grouped[rowKey] = {
        rowKey,
        data: {},
        ...Object.fromEntries(localRowFields.map(f => [f.name, item[f.name]]))
      }
    }

    const colKey = columnTreeConfig.value.map(cfg => {
      const val = item[cfg.field]
      return val != null ? String(val).trim() : 'N/A'
    }).join('|||')

    grouped[rowKey].data[colKey] = { ...item }  // last wins
  })

  pivotData.value = Object.values(grouped)
}

// Header calculations
const maxHeaderDepth = computed(() => {
  const getDepth = (nodes, depth = 1) => {
    if (!nodes?.length) return depth - 1
    return Math.max(depth, ...nodes.map(n => getDepth(n.children, depth + 1)))
  }
  return getDepth(columnHeaderTree.value) + 1
})

const getNodesAtLevel = (level) => {
  let current = columnHeaderTree.value
  for (let i = 0; i < level; i++) {
    current = current.flatMap(n => n.children || [])
  }
  return current
}

const getColspan = (node) => {
  const countLeaves = (n) => {
    if (!n.children?.length) return 1
    return n.children.reduce((sum, c) => sum + countLeaves(c), 0)
  }
  return countLeaves(node)
}

const getRowspan = (node, level) => {
  if (node.isLeaf) return 1
  if (node.children?.length) return 1
  return maxHeaderDepth.value - level
}

const allLeafColumns = computed(() => {
  const getLeaves = (nodes) => {
    return nodes.flatMap(node => {
      if (!node.children?.length) return [node]
      return getLeaves(node.children)
    })
  }
  return getLeaves(columnHeaderTree.value)
})

// Formatting
const formatCellValue = (value, field) => {
  if (value === undefined || value === null || value === '') return '-'
  
  if (field.format === 'number') {
    const num = $formatNumber(value)
    if (num === undefined) return '-'
    return $numtoString(num, 'vi-VN', field.decimal || 0, field.decimal || 0)
  }
  
  if (field.format === 'date') {
    return $dayjs(value).format('DD/MM/YYYY')
  }
  
  return value
}

// Styles
const tableStyle = computed(() => {
  const settings = pagedata.value?.tablesetting || store.tablesetting
  if (!settings) return 'font-size: 13px;'
  const bg = $find(settings, {code: 'table-background'}, 'detail') || '#ffffff'
  const color = $find(settings, {code: 'table-font-color'}, 'detail') || '#000000'
  const size = $find(settings, {code: 'table-font-size'}, 'detail') || 13
  return `background-color: ${bg}; color: ${color}; font-size: ${size}px;`
})

const headerStyle = computed(() => {
  const settings = pagedata.value?.tablesetting || store.tablesetting
  if (!settings) return 'background-color: #363636; color: white; font-weight: bold; position: sticky; top: 0; z-index: 10;'
  const bg = $find(settings, {code: 'header-background'}, 'detail') || '#363636'
  const color = $find(settings, {code: 'header-font-color'}, 'detail') || '#ffffff'
  const size = $find(settings, {code: 'header-font-size'}, 'detail') || 14
  return `background-color: ${bg}; color: ${color}; font-weight: bold; font-size: ${size}px; position: sticky; top: 0; z-index: 10; padding: 8px; cursor: pointer;`
})

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

const getHeaderStyle = (node, level) => {
  let style = headerStyle.value
  if (node.minwidth) style += ` min-width: ${node.minwidth}px;`
  return style + ' border-bottom: 1px solid #dbdbdb;'
}

const getRowCellStyle = (field, row) => {
  let style = 'padding: 8px; vertical-align: middle; border: solid 1px #dbdbdb; background-color: #f5f5f5; cursor: pointer;'
  if (field.bgcolor) style += ` background-color: ${field.bgcolor};`
  if (field.color) style += ` color: ${field.color};`
  if (field.minwidth) style += ` min-width: ${field.minwidth}px;`
  if (field.maxwidth) style += ` max-width: ${field.maxwidth}px;`
  return style
}

const getDataCellStyle = (row, leaf) => {
  let style = 'padding: 6px 8px; vertical-align: middle; border: solid 1px #dbdbdb; cursor: pointer;'
  const cell = row.data[leaf.colKey]
  
  if (cell && leaf.field) {
    if (leaf.field === 'paid_amount') {
      const amount = $formatNumber(cell.amount) || 0
      const paid = $formatNumber(cell.paid_amount) || 0
      if (paid >= amount) {
        style += ' background-color: #d4edda;'
      } else if (paid > 0) {
        style += ' background-color: #fff3cd;'
      } else {
        style += ' background-color: #f8d7da;'
      }
    }
    if (leaf.field === 'to_date') {
      const dueDate = cell.to_date
      const paid = $formatNumber(cell.paid_amount) || 0
      const amount = $formatNumber(cell.amount) || 0
      if (dueDate && paid < amount) {
        const today = new Date()
        const due = new Date(dueDate)
        if (due < today) style += ' background-color: #f8d7da; color: #721c24;'
      }
    }
  }
  return style
}

// Filter functions
const applyFilters = () => {
  if (!pagedata.value?.data) {
    pivotData.value = []
    return
  }
  
  let filtered = $copy(pagedata.value.data)
  
  filters.value.forEach(filter => {
    if (filter.select && filter.select.length > 0) {
      filtered = filtered.filter(row => {
        return filter.select.some(selectedValue => {
          const rowValue = row[filter.name]
          if (typeof selectedValue === 'undefined' || selectedValue === null || selectedValue === '') {
            return !rowValue || rowValue === '' || rowValue === null
          }
          return String(rowValue) === String(selectedValue)
        })
      })
    }
  })
  
  transformToPivot(filtered)
}

const clearFilters = () => {
  filters.value = []
  applyFilters()
}

const removeFilter = (index) => {
  $remove(filters.value, index)
  applyFilters()
}

const showCondition = (filter) => {
  // Placeholder
}

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

const doSelect = (value) => {
  showmodal.value = undefined
  const field = currentField.value
  
  if (!field) return
  
  let found = filters.value.find(v => v.name === field.name)
  if (found) {
    if (!found.select) found.select = []
    const idx = found.select.findIndex(x => String(x) === String(value))
    if (idx >= 0) {
      found.select.splice(idx, 1)
    } else {
      found.select.push(value)
    }
    
    if (found.select.length === 0) {
      const filterIdx = filters.value.findIndex(v => v.name === field.name)
      if (filterIdx >= 0) filters.value.splice(filterIdx, 1)
    }
  } else {
    const newFilter = {
      name: field.name, 
      label: field.label, 
      select: [value], 
      format: field.format
    }
    filters.value.push(newFilter)
  }
  
  filters.value = [...filters.value]
  setTimeout(() => applyFilters(), 0)
}

const doubleClick = (field, value) => {
  currentField.value = field
  doSelect(value)
}

// Event handlers
const handleCellClick = (row, leaf) => {
  const cell = row.data[leaf.colKey]
  if (!cell) return

  if (props.modal) {
    const copy = $copy(props.modal)
    const f = copy.vbind ? copy.vbind : { pagename: vpagename, api: props.api, row: cell }
    f.pagename = vpagename
    f.row = cell
    copy.vbind = f
    showmodal.value = copy
  }
}

const clickEvent = (event, row, field) => {
  const name = typeof event === "string" ? event : event.name
  const data = typeof event === "string" ? event : event.data
  
  if (name === 'remove') {
    currentRow.value = 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, data)
}

const confirmRemove = () => {
  if (currentRow.value?.id && pagedata.value?.api) {
    $deleterow(pagedata.value.api.name, currentRow.value.id, vpagename, true)
  }
}

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

// Realtime & API functions (giữ nguyên)
const generateDataHash = (data) => {
  if (!data) return null
  try {
    const replacer = (key, value) =>
      value && typeof value === 'object' && !Array.isArray(value)
        ? Object.keys(value)
            .sort()
            .reduce((sorted, key) => {
              sorted[key] = value[key]
              return sorted
            }, {})
        : value
    const stringToHash = JSON.stringify(data, replacer)
    return stringToHash.split('').reduce((a, b) => {
      a = ((a << 5) - a) + b.charCodeAt(0)
      return a & a
    }, 0)
  } catch (e) {
    console.error('Error generating data hash:', e)
    return null
  }
}

const startAutoCheck = () => {
  if (pollingInterval.value) clearInterval(pollingInterval.value)
  if (realtimeConfig.value.time && realtimeConfig.value.time > 0) {
    pollingInterval.value = setInterval(() => checkDataChanges(), realtimeConfig.value.time * 1000)
  }
}

const checkDataChanges = async () => {
  try {
    const connlist = []
    const conn1 = $findapi(props.api)

    if (vfilter) {
      const filter = $copy(conn1.params.filter) || {}
      for (const [key, value] of Object.entries(vfilter)) {
        filter[key] = value
      }
      for (const [key, value] of Object.entries(filter)) {
        if (value.toString().indexOf('$') >= 0) {
          filter[key] = store[value.replace('$', '')].id
        }
      }
      conn1.params.filter = filter
    }

    if (vparams.value) conn1.params = $copy(vparams.value)

    delete conn1.params.sort
    delete conn1.params.values

    conn1.params.summary = 'aggregate'
    conn1.params.distinct_values = JSON.stringify({
      total_count: { type: 'Count', field: 'id' },
      last_updated: { type: 'Max', field: 'update_time' },
      last_created: { type: 'Max', field: 'create_time' }
    })

    connlist.push(conn1)
    const rs = await $getapi(connlist)
    const obj = $find(rs, { name: props.api })
    const newMetadata = obj ? obj.data.rows : {}
    const newHash = generateDataHash(newMetadata)

    if (lastDataHash.value === null) {
      lastDataHash.value = newHash
      return
    }

    if (newHash !== lastDataHash.value) {
      lastDataHash.value = newHash

      if (realtimeConfig.value.update === "true") {
        await loadFullDataAsync()
        emit('dataUpdated', { newData: store[vpagename].data, autoUpdate: true, hasChanges: true })
      } else {
        newDataAvailable.value = true
        emit('dataUpdated', { newData: null, autoUpdate: false, hasChanges: true })
      }
    }
  } catch (error) {
    console.error('Error checking data:', error)
  }
}

const loadFullDataAsync = async () => {
  try {
    const connlist = []
    const conn1 = $findapi(props.api)

    if (vfilter) {
      const filter = $copy(conn1.params.filter) || {}
      for (const [key, value] of Object.entries(vfilter)) {
        filter[key] = value
      }
      for (const [key, value] of Object.entries(filter)) {
        if (value.toString().indexOf('$') >= 0) {
          filter[key] = store[value.replace('$', '')].id
        }
      }
      conn1.params.filter = filter
    }

    if (vparams.value) conn1.params = $copy(vparams.value)

    delete conn1.params.summary
    delete conn1.params.distinct_values

    connlist.push(conn1)
    const rs = await $getapi(connlist)
    const obj = $find(rs, { name: props.api })
    const newData = obj ? $copy(obj.data.rows) : []

    updateDataDisplay(newData)
  } catch (error) {
    console.error('Error loading full data:', error)
  }
}

const updateDataDisplay = (newData) => {
  const copy = $clone(store[vpagename])
  copy.data = newData
  copy.update = { data: newData }
  store.commit(vpagename, copy)
  newDataAvailable.value = false
  transformToPivot(newData)
}

const manualRefresh = () => {
  refreshData()
}

const refreshData = async () => {
  if (pollingInterval.value) clearInterval(pollingInterval.value)
  newDataAvailable.value = false
  
  await getApi()
  
  lastDataHash.value = null
  await checkDataChanges()
  
  newDataAvailable.value = false
  startAutoCheck()
}

const openImportModal = () => {
  const copy = $copy(props.importdata)
  showmodal.value = copy
}

const timeOption = (v) => {
  if (!v) return getApi()

  if (v.filter_or) {
    if (vfilter) vfilter = undefined
    if (vparams.value) {
      vparams.value.filter_or = v.filter_or
    }
    return getApi()
  }

  let filter = vfilter ? vfilter : (vparams.value ? vparams.value.filter || {} : {})
  for (const [key, value] of Object.entries(v.filter)) {
    filter[key] = value
  }

  for (const [key, value] of Object.entries(filter)) {
    if (value.toString().indexOf('$') >= 0) {
      filter[key] = store[value.replace('$', '')].id
    }
  }

  if (vfilter) {
    vfilter = filter
  } else if (vparams.value) {
    vparams.value.filter = filter
    vparams.value.filter_or = undefined
  }

  if (!vfilter && !vparams.value) vfilter = filter
  getApi()
}

const insert = () => {
  const copy = props.modal ? $copy(props.modal) : {}
  const f = copy.vbind ? copy.vbind : { pagename: vpagename, api: props.api }
  f.pagename = vpagename
  copy.vbind = f
  showmodal.value = copy
}

// API load
const getApi = async () => {
  const connlist = []
  let row = props.setting?.id ? $copy(props.setting) : undefined

  if (!row) {
    const found = $find(store.settings.filter(v => v), props.setting > 0 ? { id: props.setting } : { name: props.setting })
    if (found) row = $copy(found)
  }

  if (!row && props.setting) {
    const conn = $findapi('usersetting')
    conn.params.filter = props.setting > 0 ? { id: props.setting } : { name: props.setting }
    connlist.push(conn)
  }

  let data = props.data ? $copy(props.data) : undefined

  if (!data) {
    const conn1 = $findapi(props.api)
    if (vfilter) {
      const filter = conn1.params.filter || {}
      for (const [key, value] of Object.entries(vfilter)) {
        filter[key] = value
      }
      for (const [key, value] of Object.entries(filter)) {
        if (value.toString().indexOf('$') >= 0) {
          filter[key] = store[value.replace('$', '')].id
        }
      }
      conn1.params.filter = filter
    }
    if (vparams.value) conn1.params = vparams.value
    connection = conn1
    connlist.push(conn1)
  }

  let obj = undefined
  if (connlist.length > 0) {
    const rs = await $getapi(connlist)
    const ele = $find(rs, { name: 'usersetting' })
    if (ele) {
      row = $find(ele.data.rows, { name: props.setting.name || props.setting })
      const copy = $copy(store.settings)
      copy.push(row)
      store.commit('settings', copy)
    }
    obj = $find(rs, { name: props.api })
    if (obj) data = $copy(obj.data.rows)
  }

  pagedata.value = $setpage(vpagename, row, obj)
  const copy = $clone(pagedata.value)
  copy.data = data
  copy.update = { data: data }
  store.commit(vpagename, copy)

  transformToPivot(data)
}

const exportExcel = async () => {
  if (!props.api || !connection) return

  const found = $findapi('exportcsv')
  found.params = connection.params
  const fields = pagedata.value.fields
    .filter(v => (v.show && v.export !== 'no') || v.export === 'yes')
    .map(x => ({ name: x.name, label: $stripHtml(x.label) }))
  found.params.fields = JSON.stringify(fields)
  found.url = connection.url.replace('data/', 'exportcsv/')
  
  try {
    const rs = await $getapi([found])
    if (rs === 'error') {
      $snackbar('Đã xảy ra lỗi. Vui lòng thử lại.')
      return
    }

    const url = window.URL.createObjectURL(new Blob([rs[0].data]))
    const link = document.createElement('a')
    const fileName = `${$dayjs(new Date()).format('YYYYMMDDhhmmss')}-data.csv`
    link.href = url
    link.setAttribute('download', fileName)
    document.body.appendChild(link)
    link.click()
    link.remove()
  } catch (error) {
    console.error('Export error:', error)
    $snackbar('Lỗi xuất file. Vui lòng thử lại.')
  }
}

// Watchers
watch(() => filters.value, (newVal) => {
  console.log('[watch filters] Filters changed:', newVal)
  applyFilters()
}, { deep: true })

watch(() => store[vpagename], (newVal) => {
  if (newVal?.update?.data) {
    console.log('[watch store] Data updated')
    pagedata.value = newVal
    transformToPivot(newVal.data)
  }
}, { deep: true })

watch(() => props.realtime, (newVal) => {
  if (newVal) {
    realtimeConfig.value.time = newVal.time || 0
    realtimeConfig.value.update = newVal.update === true ? "true" : "false"
    startAutoCheck()
  }
}, { deep: true })

// Lifecycle
onMounted(async () => {
  console.log('[PivotDataView] Component mounted')
  if (!props.timeopt) await getApi()
  startAutoCheck()
})

onBeforeUnmount(() => {
  if (pollingInterval.value) clearInterval(pollingInterval.value)
})
</script>

<style scoped>
:deep(.table tbody tr:hover td) {
  background-color: hsl(0, 0%, 88%) !important;
  color: rgb(0, 0, 0);
}

:deep(.table thead th) {
  cursor: pointer;
  user-select: none;
}

:deep(.is-sticky-left) {
  position: sticky;
  left: 0;
  z-index: 1000;
}

:deep(.table-container) {
  overflow-x: auto;
  overflow-y: auto;
}

:deep(.help) {
  font-size: 0.75rem;
  display: block;
  margin-top: 0.25rem;
}

</style>