<script setup>
import { ref, watch, onMounted, onUnmounted, computed } from 'vue'
import { AgGridVue } from 'ag-grid-vue3'
import { ModuleRegistry, AllCommunityModule } from 'ag-grid-community'
import DataTableHeader from './components/DataTableHeader.vue'
import UserDetailsDialog from './components/UserDetailsDialog.vue'
import ConfirmDeleteDialog from './components/ConfirmDeleteDialog.vue'
import { useDataTableGrid } from './composables/useDataTableGrid'
import { useDataTableActions } from './composables/useDataTableActions'
import { useResponsive } from './composables/useResponsive'
import jsPDF from 'jspdf'
import autoTable from 'jspdf-autotable'

ModuleRegistry.registerModules([AllCommunityModule])

const props = defineProps({
  apiUrl: {
    type: String,
    default: ''
  },
  apiHeaders: {
    type: Object,
    default: () => ({})
  },
  data: {
    type: Array,
    default: () => []
  },
  columns: {
    type: Array,
    default: () => []
  },
  height: {
    type: String,
    default: '600px'
  },
  mobileHeight: {
    type: String,
    default: '400px'
  },
  enableExport: {
    type: Boolean,
    default: true
  },
  enableEdit: {
    type: Boolean,
    default: true
  },
  enableDelete: {
    type: Boolean,
    default: true
  },
  enableSearch: {
    type: Boolean,
    default: true
  },
  loading: {
    type: Boolean,
    default: false
  },
  customActions: {
    type: Array,
    default: () => []
  },
  dataProcessor: {
    type: Function,
    default: null
  },
  schemaMap: {
    type: Object,
    default: () => ({})
  },
  keepOriginalKeys: {
    type: Boolean,
    default: false
  },
  maxCellTextLength: {
    type: Number,
    default: 30
  },
  tableId: {
    type: String,
    default: 'default'
  }
})

const emit = defineEmits([
  'data-updated',
  'row-selected',
  'row-deleted',
  'row-edited',
  'export-completed',
  'api-error'
])

const { isMobile, isTablet, windowWidth } = useResponsive()
const gridContainer = ref(null)

const fetchedData = ref([])
const isLoading = ref(false)

const normalizeRecord = (record, index) => {
  if (!props.schemaMap || Object.keys(props.schemaMap).length === 0) {
    return record
  }
  const normalized = {}
  for (const [canonicalKey, candidates] of Object.entries(props.schemaMap)) {
    const keys = Array.isArray(candidates) ? candidates : [candidates]
    let found = undefined
    for (const k of keys) {
      if (record[k] !== undefined) {
        found = record[k]
        break
      }
    }
    if (canonicalKey === 'id' && (found === undefined || found === null)) {
      normalized[canonicalKey] = record.id ?? index + 1
    } else {
      normalized[canonicalKey] = found
    }
  }
  return props.keepOriginalKeys ? { ...record, ...normalized } : normalized
}

const processedData = computed(() => {
  const sourceData = props.data?.length ? props.data : fetchedData.value
  if (!Array.isArray(sourceData)) return []

  return sourceData.map((item, index) => {
    const base = normalizeRecord(item, index)
    return {
      ...base,
      id: base?.id ?? item?.id ?? index + 1,
    }
  })
})

// Custom cell renderer component for long text with tooltip
const LongTextCellRenderer = {
  template: `
    <div 
      :title="fullText" 
      class="long-text-cell"
      style="
        white-space: nowrap; 
        overflow: hidden; 
        text-overflow: ellipsis;
        cursor: help;
      "
    >
      {{ displayText }}
    </div>
  `,
  setup(props) {
    const fullText = props.value || ''
    const maxLength = props.colDef?.cellRendererParams?.maxLength || 30
    const displayText = fullText.length > maxLength
      ? fullText.substring(0, maxLength) + '...'
      : fullText

    return {
      fullText,
      displayText
    }
  }
}

// Dynamically infer columns from data when not provided
const inferredColumns = computed(() => {
  if (Array.isArray(props.columns) && props.columns.length) return props.columns
  const rows = Array.isArray(processedData.value) ? processedData.value.slice(0, 50) : []
  if (!rows.length) return []

  const keySet = new Set()
  rows.forEach(r => {
    if (r && typeof r === 'object') {
      Object.keys(r).forEach(k => {
        if (k !== 'action' && r[k] !== undefined && typeof r[k] !== 'object') keySet.add(k)
      })
    }
  })

  const keys = Array.from(keySet).filter(k => k !== 'action')
  const toTitle = (k) => k
    .replace(/_/g, ' ')
    .replace(/([a-z])([A-Z])/g, '$1 $2')
    .replace(/^./, s => s.toUpperCase())

  const isLikelyDate = (v) => {
    if (typeof v !== 'string') return false
    if (/^\d{4}-\d{2}-\d{2}/.test(v) || /T\d{2}:\d{2}:\d{2}/.test(v)) return true
    const d = new Date(v)
    return !isNaN(d.getTime())
  }

  const isLongText = (samples) => {
    return samples.some(val =>
      typeof val === 'string' && val.length > props.maxCellTextLength
    )
  }

  const columns = keys.map(k => {
    const sampleValues = []
    for (let i = 0; i < Math.min(10, rows.length); i++) {
      const val = rows[i]?.[k]
      if (val !== undefined && val !== null) {
        sampleValues.push(val)
      }
    }

    const sampleVal = sampleValues[0]

    const col = {
      field: k,
      headerName: toTitle(k),
      sortable: true,
      filter: true,
      editable: false,
      minWidth: 100,
      flex: 1,
      suppressMovable: true
    }

    if (typeof sampleVal === 'number') {
      col.filter = true
      col.width = 120
      col.valueFormatter = p => (typeof p.value === 'number' ? p.value : p.value ?? '')
    } else if (typeof sampleVal === 'boolean') {
      col.width = 120
      col.valueFormatter = p => (p.value ? 'Yes' : 'No')
    } else if (isLikelyDate(sampleVal)) {
      col.width = 140
      col.valueFormatter = p => {
        if (!p.value) return ''
        const d = new Date(p.value)
        return isNaN(d.getTime()) ? String(p.value) : d.toISOString().split('T')[0]
      }
    } else if (typeof sampleVal === 'string' && isLongText(sampleValues)) {
      col.flex = 1
      col.cellRenderer = LongTextCellRenderer
      col.cellRendererParams = {
        maxLength: props.maxCellTextLength
      }
      col.tooltipField = k
    } else {
      col.flex = 1
    }

    return col
  })

  columns.push({
    headerName: "ACTION",
    field: "action",
    sortable: false,
    filter: false,
    flex: 1,
    minWidth: 100,
    hide: false,
    editable: false,
    cellRenderer: actionButtonsRenderer,
    suppressHeaderMenuButton: true,
    suppressMovable: true,
    lockPosition: true,
    colId: "action"
  })

  return columns
})


let gridSetup = useDataTableGrid(processedData, isMobile, isTablet, inferredColumns, props.tableId)
console.log('AgGridTable props.tableId:', props.tableId);
const gridApi = gridSetup.gridApi
const columnDefs = gridSetup.columnDefs
const rowData = gridSetup.rowData
const defaultColDef = gridSetup.defaultColDef
const gridOptions = gridSetup.gridOptions
const gridReady = gridSetup.onGridReady
const onCellValueChanged = gridSetup.onCellValueChanged
const updateColumnVisibility = gridSetup.updateColumnVisibility
const updatePagination = gridSetup.updatePagination

watch(
  processedData,
  (newData) => {
    if (Array.isArray(newData) && gridApi.value) {
      try {
        if (typeof gridApi.value.setRowData === 'function') {
          gridApi.value.setRowData(newData)
        } else if (typeof gridApi.value.setGridOption === 'function') {
          gridApi.value.setGridOption("rowData", newData)
        }
      } catch (error) {
        console.warn('Error updating grid data:', error)
      }
    }
  },
  { deep: true, immediate: true }
)

const fetchData = async () => {
  if (!props.apiUrl) return

  isLoading.value = true
  try {
    const response = await fetch(props.apiUrl, {
      headers: {
        'Content-Type': 'application/json',
        ...props.apiHeaders
      }
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    const result = await response.json()

    let processedApiData = []
    if (props.dataProcessor && typeof props.dataProcessor === 'function') {
      processedApiData = props.dataProcessor(result)
    } else {
      processedApiData = result.todos || result.data || result.users || result.products || (Array.isArray(result) ? result : [])
    }

    fetchedData.value = Array.isArray(processedApiData) ? processedApiData : []
    console.log('Fetched data set to:', fetchedData.value)
    emit('data-updated', fetchedData.value)
  } catch (error) {
    console.error('Error fetching data:', error)
    emit('api-error', error)
  } finally {
    isLoading.value = false
  }
}

const saveToAPI = async (userData, isUpdate = false) => {
  if (!props.apiUrl) return

  try {
    const method = isUpdate ? 'PUT' : 'POST'
    const url = isUpdate ? `${props.apiUrl}/${userData.id}` : props.apiUrl

    const response = await fetch(url, {
      method,
      headers: {
        'Content-Type': 'application/json',
        ...props.apiHeaders
      },
      body: JSON.stringify(userData)
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    await fetchData()
    return await response.json()
  } catch (error) {
    console.error('Error saving data:', error)
    emit('api-error', error)
    throw error
  }
}

const deleteFromAPI = async (id) => {
  if (!props.apiUrl) return

  try {
    const response = await fetch(`${props.apiUrl}/${id}`, {
      method: 'DELETE',
      headers: {
        'Content-Type': 'application/json',
        ...props.apiHeaders
      }
    })

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    await fetchData()
    return true
  } catch (error) {
    console.error('Error deleting data:', error)
    emit('api-error', error)
    throw error
  }
}

const {
  setupGlobalHandlers,
  cleanupGlobalHandlers,
  exportToCSV,
  exportToExcel,
  saveUser,
  confirmDelete,
  cancelDelete,
  showDetailsDialog,
  selectedRowData,
  deleteRowData,
  showDeleteDialog,
  deleteRow
} = useDataTableActions(gridApi, rowData, props.tableId)

const handleSaveUser = async (updatedData) => {
  try {
    // Optimistic update: update grid first
    saveUser(updatedData)

    if (props.apiUrl) {
      try {
        await saveToAPI(updatedData, updatedData.id ? true : false)
      } catch (apiErr) {
        console.error('API save failed, refetching to sync:', apiErr)
        // Re-sync from server on failure
        await fetchData()
        emit('api-error', apiErr)
        return
      }
    }
    emit('row-edited', updatedData)
  } catch (error) {
    console.error('Error saving user:', error)
  }
}

const handleConfirmDelete = async () => {
  try {
    // Optimistic remove from grid first
    confirmDelete()

    if (props.apiUrl && deleteRowData.value?.id) {
      try {
        await deleteFromAPI(deleteRowData.value.id)
      } catch (apiErr) {
        console.error('API delete failed, refetching to restore:', apiErr)
        await fetchData()
        emit('api-error', apiErr)
        return
      }
    }
    emit('row-deleted', deleteRowData.value)
  } catch (error) {
    console.error('Error deleting row:', error)
  }
}

const quickFilter = ref('')

watch(quickFilter, (newValue) => {
  if (gridApi.value) {
    gridApi.value.setGridOption('quickFilterText', newValue || '')
  }
}, { immediate: false })

const onGridReady = params => {
  gridReady(params)
  setupGlobalHandlers()

  setTimeout(() => {
    const allPageSizeWrappers = document.querySelectorAll('.ag-paging-page-size .ag-picker-field-wrapper');
    allPageSizeWrappers.forEach((pageSizeWrapper, index) => {
      if (!pageSizeWrapper.hasAttribute('data-fixed')) {
        pageSizeWrapper.setAttribute('data-fixed', 'true');

        pageSizeWrapper.addEventListener('mousedown', (e) => {
          if (e.button !== 0) {
            e.preventDefault();
            e.stopPropagation();
            return;
          }
          e.preventDefault();
          e.stopPropagation();
          const isExpanded = pageSizeWrapper.getAttribute('aria-expanded') === 'true';
          pageSizeWrapper.setAttribute('aria-expanded', !isExpanded);
          pageSizeWrapper.classList.toggle('ag-picker-collapsed', isExpanded);
          pageSizeWrapper.classList.toggle('ag-picker-expanded', !isExpanded);
          const selectList = pageSizeWrapper.parentNode.querySelector('[id^="ag-select-list"]');
          if (selectList) {
            selectList.style.display = isExpanded ? 'none' : 'block';
          }
        });

        pageSizeWrapper.addEventListener('contextmenu', (e) => {
          e.preventDefault();
          e.stopPropagation();
        });
      }
    });
  }, 1000);

  if (props.apiUrl) {
    fetchData()
  }

  try {
    const container = gridContainer?.value
    if (container && typeof ResizeObserver !== 'undefined') {
      const ro = new ResizeObserver(() => {
        if (gridApi.value) {
          updatePagination()
          setTimeout(() => {
            gridApi.value?.sizeColumnsToFit?.()
          }, 50)
        }
      })
      ro.observe(container)
    }
  } catch (e) { }
}

const handleQuickFilterUpdate = (value) => {
  quickFilter.value = value || ''

  if (gridApi.value) {
    gridApi.value.setGridOption('quickFilterText', value || '')
  }
}

const refreshData = () => {
  if (props.apiUrl) {
    fetchData()
  }
}

defineExpose({
  refreshData,
  fetchData,
  gridApi
})

onMounted(() => {
  setupGlobalHandlers()
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  cleanupGlobalHandlers()
  window.removeEventListener('resize', handleResize)
})

const handleResize = () => {
  windowWidth.value = window.innerWidth
  if (gridApi.value) {
    updateColumnVisibility()
    updatePagination()
    setTimeout(() => {
      if (gridApi.value) {
        gridApi.value.sizeColumnsToFit();
      }
    }, 100)
  }
}


const exportToPDF = () => {
  if (!gridApi.value) {
    console.error('Grid API not available')
    return
  }

  try {
    const doc = new jsPDF()
    const rowDataForPDF = []
    const headers = []

    columnDefs.value.forEach(col => {
      if (col.headerName && col.field && col.field !== 'action') {
        headers.push(col.headerName)
      }
    })

    gridApi.value.forEachNodeAfterFilterAndSort(node => {
      const row = []
      columnDefs.value.forEach(col => {
        if (col.field && col.field !== 'action') {
          let value = node.data[col.field] || ''
          row.push(value)
        }
      })
      rowDataForPDF.push(row)
    })

    autoTable(doc, {
      head: [headers],
      body: rowDataForPDF,
      styles: {
        fontSize: 8,
        cellPadding: 2
      },
      headStyles: {
        fillColor: [41, 128, 185],
        textColor: 255,
        fontStyle: 'bold'
      },
      alternateRowStyles: {
        fillColor: [245, 245, 245]
      },
      margin: { top: 20 }
    })

    doc.save(`data-table-${new Date().toISOString().split('T')[0]}.pdf`)
    emit('export-completed', 'pdf')
  } catch (error) {
    console.error('Error exporting PDF:', error)
  }
}
</script>

<template>
  <v-card class="elevation-2">
    <v-overlay v-model="isLoading" contained class="align-center justify-center">
      <v-progress-circular indeterminate size="64" color="primary"></v-progress-circular>
    </v-overlay>

    <DataTableHeader v-if="enableSearch || enableExport" :is-mobile="isMobile" :is-tablet="isTablet"
      :quick-filter="quickFilter" :enable-search="enableSearch" :enable-export="enableExport"
      @update:quick-filter="handleQuickFilterUpdate" @export-csv="exportToCSV" @export-excel="exportToExcel"
      @export-pdf="exportToPDF" />

    <v-divider v-if="enableSearch || enableExport" />

    <v-card-text class="pa-0">
      <AgGridVue ref="gridContainer" class="vuetify-grid ag-theme-alpine-dark"
        :style="`height:${isMobile ? mobileHeight : height}; width:100%`" :columnDefs="columnDefs"
        :rowData="processedData" :defaultColDef="defaultColDef" :gridOptions="gridOptions" @grid-ready="onGridReady"
        @cell-value-changed="onCellValueChanged" />
    </v-card-text>

    <UserDetailsDialog v-if="enableEdit" v-model="showDetailsDialog" :selected-row-data="selectedRowData"
      :columns="columnDefs" :is-mobile="isMobile" @save-user="handleSaveUser" />

    <ConfirmDeleteDialog v-if="enableDelete" v-model="showDeleteDialog" :selected-row-data="deleteRowData"
      :columns="columnDefs" :is-mobile="isMobile" @confirm="handleConfirmDelete" @cancel="cancelDelete" />
  </v-card>
</template>
