<script setup lang="ts">
import { computed } from 'vue'
import { AgGridVue } from 'ag-grid-vue3'
import {
  ClientSideRowModelModule,
  ColumnAutoSizeModule,
  CellStyleModule,
  ValidationModule,
  TextFilterModule,
  TextEditorModule,
  NumberEditorModule,
  SelectEditorModule,
  UndoRedoEditModule,
  RowStyleModule,
  RenderApiModule,
  ModuleRegistry,
  RowSelectionModule,
  ScrollApiModule,
  themeQuartz,
  type ColDef,
  type RowClickedEvent,
  type CellValueChangedEvent,
  type RowClassParams
} from 'ag-grid-community'
import type { UiGridColumn } from '../adapter/contracts'

ModuleRegistry.registerModules([
  ClientSideRowModelModule,
  ColumnAutoSizeModule,
  CellStyleModule,
  ValidationModule,
  TextFilterModule,
  TextEditorModule,
  NumberEditorModule,
  SelectEditorModule,
  UndoRedoEditModule,
  RowStyleModule,
  RenderApiModule,
  RowSelectionModule,
  ScrollApiModule
])



const emit = defineEmits<{ rowSelected: [row: unknown]; cellValueChanged: [event: CellValueChangedEvent] }>()
const props = withDefaults(defineProps<{ rows: unknown[]; columns: UiGridColumn[]; loading?: boolean; height?: string; rowSelection?: 'single' | 'multiple' | 'none'; showRowNumber?: boolean }>(), { loading: false, height: '32rem', rowSelection: 'single', showRowNumber: true })

const columnDefs = computed<ColDef[]>(() => {
  const mappedCols: ColDef[] = props.columns.map(column => ({
    field: column.field,
    headerName: column.header,
    width: column.width,
    minWidth: column.minWidth ?? 80,
    maxWidth: column.maxWidth,
    flex: column.flex ?? (column.width ? undefined : 1),

    sortable: column.sortable ?? true,
    filter: column.filterable ?? false,
    editable: column.editable ?? false,

    valueFormatter: column.formatter ? params => column.formatter?.(params.value, params.data) ?? '' : undefined
  }))

  if (props.showRowNumber) {
    const rowNumCol: ColDef = {
      headerName: 'No.',
      valueGetter: 'node.rowIndex + 1',
      colId: 'rowNum',
      width: 54,
      minWidth: 50,
      maxWidth: 65,
      pinned: 'left',
      sortable: false,
      filter: false,
      resizable: false,
      editable: false,
      cellStyle: { textAlign: 'center', color: 'var(--ks-color-neutral-600)', fontSize: '11px' },
      headerClass: 'ks-row-number-header'
    }
    return [rowNumCol, ...mappedCols]
  }

  return mappedCols
})

const rowClassRules = computed(() => ({
  'ks-row-added': (params: RowClassParams) => {
    const data = params.data as Record<string, unknown> | undefined
    if (!data) return false
    return !!(data._isNew || (typeof data.code === 'string' && data.code.startsWith('NEW_')) || (typeof data.groupCode === 'string' && data.groupCode.startsWith('NEW_')))
  },
  'ks-row-modified': (params: RowClassParams) => {
    const data = params.data as Record<string, unknown> | undefined
    return data?._isModified === true
  }
}))

const rowSelectionOptions = computed(() => {
  if (props.rowSelection === 'none') return undefined
  if (props.rowSelection === 'multiple') return { mode: 'multiRow' } as const
  return { mode: 'singleRow', checkboxes: false, enableClickSelection: true } as const
})

function onRowClicked(event: RowClickedEvent): void {
  if (event.data) emit('rowSelected', event.data)
}

function onCellValueChanged(event: CellValueChangedEvent): void {
  if (event.data) {
    (event.data as Record<string, unknown>)._isModified = true
    emit('cellValueChanged', event)
  }
}

function onCellKeyDown(event: any): void {
  if (event.event.key === 'Enter' && event.colDef?.editable) {
    event.event.preventDefault()
    event.api.stopEditing(false)
    event.api.tabToNextCell(false)

    const attempt = () => {
      const focusedCell = event.api.getFocusedCell()
      if (focusedCell) {
        const col = focusedCell.column.getColId()
        const rowIndex = focusedCell.rowIndex
        event.api.ensureIndexVisible(rowIndex)
        event.api.setFocusedCell(rowIndex, col)
        event.api.startEditingCell({ rowIndex, colKey: col })
      }
    }

    attempt()
    setTimeout(attempt, 10)
    setTimeout(attempt, 50)
  }
}

function onSortChanged(params: { api: { redrawRows: () => void; refreshCells: (options: { force: boolean }) => void } }): void {
  params.api.redrawRows()
}

import { ref, onMounted, onUnmounted } from 'vue'
import type { GridApi, GridReadyEvent } from 'ag-grid-community'

const gridApi = ref<GridApi | null>(null)

function handleResize() {
  gridApi.value?.sizeColumnsToFit()
}

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

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

function onGridReady(params: GridReadyEvent): void {
  gridApi.value = params.api
  params.api.sizeColumnsToFit()
}

function focusRow(rowIndex: number, colKey?: string): void {
  if (!gridApi.value) return
  const col = colKey || (props.columns[0]?.field ?? '')

  gridApi.value.ensureIndexVisible(rowIndex)

  const attempt = () => {
    if (!gridApi.value) return
    gridApi.value.setFocusedCell(rowIndex, col)
    gridApi.value.startEditingCell({ rowIndex, colKey: col })
  }

  attempt()
  setTimeout(attempt, 10)
  setTimeout(attempt, 50)
}

function redrawRows(): void {
  gridApi.value?.redrawRows()
}


defineExpose({ focusRow, redrawRows, gridApi })
</script>

<template>
  <div class="ks-grid" :style="{ height: props.height ?? '100%' }" :aria-busy="props.loading">
    <AgGridVue
      style="height: 100%; width: 100%; flex: 1; min-height: 0;"
      :theme="themeQuartz"
      :row-data="props.rows"
      :column-defs="columnDefs"
      :row-selection="rowSelectionOptions"
      :row-class-rules="rowClassRules"
      :enter-navigates-to-next-cell="true"
      :enter-navigates-to-next-cell-after-edit="true"
      :stop-editing-when-cells-lose-focus="true"
      :loading="props.loading"
      overlay-no-rows-template="<div class='ks-ag-empty-overlay'><span style='font-size:2rem;'>📭</span><p style='margin-top:8px;font-weight:600;color:var(--ks-color-neutral-700);'>조회된 데이터가 없습니다</p><p style='font-size:12px;color:var(--ks-color-neutral-500);'>검색 조건을 변경하거나 신규 데이터를 수집하세요.</p></div>"
      @grid-ready="onGridReady"
      @first-data-rendered="onGridReady"
      @sort-changed="onSortChanged"
      @row-clicked="onRowClicked"
      @cell-value-changed="onCellValueChanged"
      @cell-key-down="onCellKeyDown"
    />
  </div>
</template>




<style scoped>
.ks-grid {
  flex: 1;
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
  min-height: 0;

  border: 1px solid var(--ks-color-neutral-200);
  border-radius: var(--ks-radius-md);
  overflow: hidden;
  background: #fff;
}

:deep(.ag-row.ks-row-added) {
  background-color: #f0fdf4 !important;
  color: #15803d !important;
  font-weight: 600;
}

:deep(.ag-row.ks-row-modified) {
  background-color: #fffbeb !important;
  color: #b45309 !important;
  font-weight: 600;
}

:deep(.ag-cell-editable) {
  background-color: #fafafa;
  cursor: text;
  transition: background-color 0.15s ease;
}

:deep(.ag-cell-editable:hover) {
  background-color: #f0f9ff !important;
}

:deep(.ag-cell-inline-editing) {
  background-color: #ffffff !important;
  box-shadow: inset 0 0 0 2px var(--ks-color-action, #0284c7) !important;
}
</style>


