<script setup lang="ts">
import { computed, defineComponent, h, onActivated, onBeforeUnmount, onMounted, ref, watch, type PropType } from 'vue'
import { useI18n } from 'vue-i18n'
import { AgGridVue } from 'ag-grid-vue3'
import type { ColDef, GridApi, GridOptions, GridReadyEvent, ICellRendererParams } from 'ag-grid-community'
import { AG_GRID_LOCALE_CN } from '@ag-grid-community/locale'
import { myTheme, gridOptions, agGridWrapClass, agGridStyle } from '@/lib/diyAgGridOptions'
import { withReadonlyAutoHeight } from '@/lib/agGridReadonlyAutoHeight'
import { parseNumberOrNull } from '@/lib/number'
import { formatThousandsFlexible } from '@/lib/numberFormat'
import { roundTo, toDecimal } from '@/lib/decimal'
import { Button } from '@/components/ui/button'
import { useZxFwPricingStore } from '@/pinia/zxFwPricing'
import { Trash2 } from 'lucide-vue-next'
import {
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogOverlay,
  AlertDialogPortal,
  AlertDialogRoot,
  AlertDialogTitle
} from 'reka-ui'

interface FeeRow {
  id: string
  feeItem: string
  unit: string
  quantity: number | null
  unitPrice: number | null
  budgetFee: number | null
  remark: string
  actions?: unknown
}

interface FeeGridState {
  detailRows: FeeRow[]
}

const props = defineProps<{
  title: string
  storageKey: string
  htMainStorageKey?: string
  htRowId?: string
  htMethodType?: 'quantity-unit-price-fee'
}>()
const zxFwPricingStore = useZxFwPricingStore()
const { t } = useI18n()

const createRowId = () => `fee-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`
const SUBTOTAL_ROW_ID = 'fee-subtotal-fixed'

const createDefaultRow = (): FeeRow => ({
  id: createRowId(),
  feeItem: '',
  unit: '',
  quantity: null,
  unitPrice: null,
  budgetFee: null,
  remark: ''
})

const createSubtotalRow = (): FeeRow => ({
  id: SUBTOTAL_ROW_ID,
  feeItem: t('htFeeDetail.subtotal'),
  unit: '',
  quantity: null,
  unitPrice: null,
  budgetFee: 0,
  remark: ''
})

const isSubtotalRow = (row?: FeeRow | null) => row?.id === SUBTOTAL_ROW_ID

const ensureSubtotalRow = (rows: FeeRow[]) => {
  const normalRows = rows.filter(row => !isSubtotalRow(row))
  if (normalRows.length === 0) return []
  return [...normalRows, createSubtotalRow()]
}

const fallbackDetailRows = ref<FeeRow[]>([])
const gridApi = ref<GridApi<FeeRow> | null>(null)
const deleteConfirmOpen = ref(false)
const pendingDeleteRowId = ref<string | null>(null)
const pendingDeleteRowName = ref('')
const useHtMethodState = computed(
  () => Boolean(props.htMainStorageKey && props.htRowId && props.htMethodType)
)
const detailRows = computed<FeeRow[]>({
  get: () => {
    if (!useHtMethodState.value) return fallbackDetailRows.value
    const state = zxFwPricingStore.getHtFeeMethodState<FeeGridState>(
      props.htMainStorageKey!,
      props.htRowId!,
      props.htMethodType!
    )
    const rows = state?.detailRows
    return Array.isArray(rows) ? rows : []
  },
  set: rows => {
    if (!useHtMethodState.value) {
      fallbackDetailRows.value = rows
      return
    }
    zxFwPricingStore.setHtFeeMethodState(props.htMainStorageKey!, props.htRowId!, props.htMethodType!, {
      detailRows: rows
    })
  }
})

const addRow = () => {
  const normalRows = detailRows.value.filter(row => !isSubtotalRow(row))
  detailRows.value = ensureSubtotalRow([...normalRows, createDefaultRow()])
  syncComputedValuesToRows()
  void saveToIndexedDB()
}

const deleteRow = (id: string) => {
  const normalRows = detailRows.value.filter(row => !isSubtotalRow(row) && row.id !== id)
  detailRows.value = ensureSubtotalRow(normalRows)
  syncComputedValuesToRows()
  void saveToIndexedDB()
}

const requestDeleteRow = (id: string, name?: string) => {
  pendingDeleteRowId.value = id
  pendingDeleteRowName.value = String(name || '').trim() || t('htFeeDetail.currentRow')
  deleteConfirmOpen.value = true
}

const handleDeleteConfirmOpenChange = (open: boolean) => {
  deleteConfirmOpen.value = open
}

const confirmDeleteRow = () => {
  const id = pendingDeleteRowId.value
  if (!id) return
  deleteRow(id)
  deleteConfirmOpen.value = false
  pendingDeleteRowId.value = null
  pendingDeleteRowName.value = ''
}

const formatEditableText = (params: any) => {
  if (isSubtotalRow(params.data)) return ''
  if (params.value == null || params.value === '') return t('htFeeDetail.clickToInput')
  return String(params.value)
}

const formatEditableQuantity = (params: any) => {
  if (isSubtotalRow(params.data)) return ''
  if (params.value == null || params.value === '') return t('htFeeDetail.clickToInput')
  return formatThousandsFlexible(params.value, 3)
}

const formatEditableUnitPrice = (params: any) => {
  if (isSubtotalRow(params.data)) return ''
  if (params.value == null || params.value === '') return t('htFeeDetail.clickToInput')
  return formatThousandsFlexible(params.value, 3)
}

const formatReadonlyBudgetFee = (params: any) => {
  if (params.value == null || params.value === '') return ''
  return formatThousandsFlexible(params.value, 3)
}

const syncComputedValuesToRows = () => {
  let totalBudgetFee = 0
  let hasValidRow = false
  for (const row of detailRows.value) {
    if (isSubtotalRow(row)) continue
    const hasFeeItem = typeof row.feeItem === 'string' && row.feeItem.trim() !== ''
    const hasUnit = typeof row.unit === 'string' && row.unit.trim() !== ''
    const quantity = typeof row.quantity === 'number' && Number.isFinite(row.quantity) ? row.quantity : null
    const unitPrice = typeof row.unitPrice === 'number' && Number.isFinite(row.unitPrice) ? row.unitPrice : null
    if (!hasFeeItem || !hasUnit || quantity == null || unitPrice == null) {
      row.budgetFee = null
      continue
    }
    row.budgetFee = roundTo(toDecimal(quantity).mul(unitPrice), 2)
    if (typeof row.budgetFee === 'number' && Number.isFinite(row.budgetFee)) {
      hasValidRow = true
      totalBudgetFee = roundTo(toDecimal(totalBudgetFee).add(row.budgetFee), 2)
    }
  }
  const subtotalRow = detailRows.value.find(row => isSubtotalRow(row))
  if (subtotalRow) {
    subtotalRow.feeItem = t('htFeeDetail.subtotal')
    subtotalRow.unit = ''
    subtotalRow.quantity = null
    subtotalRow.unitPrice = null
    subtotalRow.budgetFee = hasValidRow ? totalBudgetFee : null
    subtotalRow.remark = ''
  }
}

const mergeWithStoredRows = (rowsFromDb: unknown): FeeRow[] => {
  if (!Array.isArray(rowsFromDb) || rowsFromDb.length === 0) {
    return []
  }

  const rows: FeeRow[] = rowsFromDb.map(item => {
    const row = item as Partial<FeeRow>
    return {
      id: typeof row.id === 'string' && row.id ? row.id : createRowId(),
      feeItem: typeof row.feeItem === 'string' ? row.feeItem : '',
      unit: typeof row.unit === 'string' ? row.unit : '',
      quantity: typeof row.quantity === 'number' ? row.quantity : null,
      unitPrice: typeof row.unitPrice === 'number' ? row.unitPrice : null,
      budgetFee: typeof row.budgetFee === 'number' ? row.budgetFee : null,
      remark: typeof row.remark === 'string' ? row.remark : ''
    }
  })

  return ensureSubtotalRow(rows)
}

const buildPersistDetailRows = () => {
  syncComputedValuesToRows()
  return detailRows.value.map(row => ({ ...row }))
}

const saveToIndexedDB = async () => {
  try {
    const payload: FeeGridState = {
      detailRows: JSON.parse(JSON.stringify(buildPersistDetailRows()))
    }
    if (useHtMethodState.value) {
      zxFwPricingStore.setHtFeeMethodState(
        props.htMainStorageKey!,
        props.htRowId!,
        props.htMethodType!,
        payload,
        { force: true }
      )
    } else {
      zxFwPricingStore.setKeyState(props.storageKey, payload)
    }
  } catch (error) {
    console.error('saveToIndexedDB failed:', error)
  }
}

const loadFromIndexedDB = async () => {
  try {
    const data = useHtMethodState.value
      ? await zxFwPricingStore.loadHtFeeMethodState<FeeGridState>(
          props.htMainStorageKey!,
          props.htRowId!,
          props.htMethodType!
        )
      : await zxFwPricingStore.loadKeyState<FeeGridState>(props.storageKey)
    detailRows.value = mergeWithStoredRows(data?.detailRows)
    syncComputedValuesToRows()
  } catch (error) {
    console.error('loadFromIndexedDB failed:', error)
    detailRows.value = []
    syncComputedValuesToRows()
  }
}

const columnDefs: ColDef<FeeRow>[] = [
  {
    headerName: t('htFeeDetail.columns.no'),
    colId: 'rowNo',
    minWidth: 68,
    maxWidth: 80,
    flex: 0.6,
    editable: false,
    sortable: false,
    filter: false,
    cellStyle: { textAlign: 'center' },
    valueGetter: params =>
      params.node?.rowPinned
        ? ''
        : isSubtotalRow(params.data)
          ? t('htFeeDetail.subtotal')
        : typeof params.node?.rowIndex === 'number'
          ? params.node.rowIndex + 1
          : '',
    colSpan: params => (isSubtotalRow(params.data) ? 2 : 1)
  },
  {
    headerName: t('htFeeDetail.columns.feeItem'),
    field: 'feeItem',
    minWidth: 140,
    flex: 1.4,
    editable: params => !isSubtotalRow(params.data),
    valueGetter: params => (isSubtotalRow(params.data) ? '' : (params.data?.feeItem ?? '')),
    valueFormatter: formatEditableText,
    cellClass: params => (isSubtotalRow(params.data) ? '' : 'editable-cell-line'),
    cellClassRules: {
      'editable-cell-empty': params => params.value == null || params.value === ''
    }
  },
  {
    headerName: t('htFeeDetail.columns.unit'),
    field: 'unit',
    minWidth: 90,
    flex: 0.9,
    editable: params => !isSubtotalRow(params.data),
    valueFormatter: formatEditableText,
    cellClass: 'editable-cell-line',
    cellClassRules: {
      'editable-cell-empty': params => params.value == null || params.value === ''
    }
  },
  {
    headerName: t('htFeeDetail.columns.quantity'),
    field: 'quantity',
    minWidth: 100,
    flex: 1,
    headerClass: 'ag-right-aligned-header',
    cellClass: 'editable-cell-line',
    editable: params => !isSubtotalRow(params.data),
    valueParser: params => parseNumberOrNull(params.newValue, { precision: 3 }),
    valueFormatter: formatEditableQuantity,
    cellClassRules: {
      'ag-right-aligned-cell': () => true,
      'editable-cell-empty': params => params.value == null || params.value === ''
    }
  },
  {
    headerName: t('htFeeDetail.columns.unitPrice'),
    field: 'unitPrice',
    minWidth: 120,
    flex: 1.1,
    headerClass: 'ag-right-aligned-header',
    cellClass: 'editable-cell-line',
    editable: params => !isSubtotalRow(params.data),
    valueParser: params => parseNumberOrNull(params.newValue, { precision: 2 }),
    valueFormatter: formatEditableUnitPrice,
    cellClassRules: {
      'ag-right-aligned-cell': () => true,
      'editable-cell-empty': params => params.value == null || params.value === ''
    }
  },
  {
    headerName: t('htFeeDetail.columns.budgetFee'),
    field: 'budgetFee',
    minWidth: 130,
    flex: 1.2,
    headerClass: 'ag-right-aligned-header',
    editable: false,
    cellClassRules: {
      'ag-right-aligned-cell': () => true
    },
    valueFormatter: formatReadonlyBudgetFee
  },
  {
    headerName: t('htFeeDetail.columns.remark'),
    field: 'remark',
    minWidth: 170,
    flex: 2,
    editable: params => !isSubtotalRow(params.data),
    cellEditor: 'agLargeTextCellEditor',
    wrapText: true,
    autoHeight: true,
    cellStyle: { whiteSpace: 'normal', lineHeight: '1.4' },
    valueFormatter: formatEditableText,
    cellClass: ' remark-wrap-cell',
    cellClassRules: {
      'editable-cell-empty': params => params.value == null || params.value === ''
    }
  },
  {
    headerName: t('htFeeDetail.columns.actions'),
    field: 'actions',
    minWidth: 92,
    maxWidth: 110,
    flex: 0.8,
    editable: false,
    sortable: false,
    filter: false,
    suppressMovable: true,
    cellRenderer: defineComponent({
      name: 'HtFeeGridActionCellRenderer',
      props: {
        params: {
          type: Object as PropType<ICellRendererParams<FeeRow>>,
          required: true
        }
      },
      setup(rendererProps) {
        return () => {
          const row = rendererProps.params.data
          if (!row || isSubtotalRow(row)) return null
          const onDelete = (event: MouseEvent) => {
            event.preventDefault()
            event.stopPropagation()
            requestDeleteRow(row.id, row.feeItem)
          }
          return h(
            'button',
            {
              type: 'button',
              class:
                'inline-flex cursor-pointer items-center gap-1 rounded border border-red-200 px-2 py-1 text-xs text-red-600 hover:bg-red-50',
              onClick: onDelete
            },
            [h(Trash2, { size: 12, 'aria-hidden': 'true' }), h('span', t('common.delete'))]
          )
        }
      }
    })
  }
]
const gridColumnDefs = computed(() => withReadonlyAutoHeight(columnDefs))

const detailGridOptions: GridOptions<FeeRow> = {
  ...gridOptions,
  treeData: false
}

const handleGridReady = (event: GridReadyEvent<FeeRow>) => {
  gridApi.value = event.api
}

let isBulkClipboardMutation = false

const commitGridChanges = () => {
  syncComputedValuesToRows()
  gridApi.value?.refreshCells({ force: true })
  void saveToIndexedDB()
}

const handleCellValueChanged = () => {
  if (isBulkClipboardMutation) return
  commitGridChanges()
}

const handleBulkMutationStart = () => {
  isBulkClipboardMutation = true
}

const handleBulkMutationEnd = () => {
  isBulkClipboardMutation = false
  commitGridChanges()
}

const processCellForClipboard = (params: any) => {
  if (Array.isArray(params.value)) {
    return JSON.stringify(params.value)
  }
  return params.value
}

const processCellFromClipboard = (params: any) => {
  const field = String(params.column?.getColDef?.().field || '')
  if (field === 'quantity') {
    return parseNumberOrNull(params.value, { precision: 3 })
  }
  if (field === 'unitPrice') {
    return parseNumberOrNull(params.value, { precision: 2 })
  }
  try {
    const parsed = JSON.parse(params.value)
    if (Array.isArray(parsed)) return parsed
  } catch (_error) {
    return params.value
  }
  return params.value
}

onMounted(async () => {
  await loadFromIndexedDB()
})

onActivated(() => {
  void loadFromIndexedDB()
})

watch(
  () => props.storageKey,
  () => {
    void loadFromIndexedDB()
  }
)

onBeforeUnmount(() => {
  gridApi.value?.stopEditing()
  gridApi.value = null
  void saveToIndexedDB()
})
</script>

<template>
  <div class="h-full min-h-0 flex flex-col">
    <div class="rounded-lg border bg-card xmMx flex min-h-0 flex-1 flex-col">
      <div class="flex items-center justify-between border-b px-4 py-3">
        <h3 class="text-sm font-semibold text-foreground">{{ title }}</h3>
        <Button type="button" variant="outline" size="sm" @click="addRow">{{ t('htFeeDetail.addRow') }}</Button>
      </div>

      <div :class="agGridWrapClass">
        <AgGridVue
          :style="agGridStyle"
          :rowData="detailRows"
          :columnDefs="gridColumnDefs"
          :gridOptions="detailGridOptions"
          :theme="myTheme"
          :animateRows="true"
          :treeData="false"
          :localeText="AG_GRID_LOCALE_CN"
          :tooltipShowDelay="500"
          :headerHeight="50"
          :suppressColumnVirtualisation="true"
          :suppressRowVirtualisation="true"
          :cellSelection="{ handle: { mode: 'range' } }"
          :enableClipboard="true"
          :processCellForClipboard="processCellForClipboard"
          :processCellFromClipboard="processCellFromClipboard"
          :undoRedoCellEditing="true"
          :undoRedoCellEditingLimit="20"
          @grid-ready="handleGridReady"
          @cell-value-changed="handleCellValueChanged"
          @paste-start="handleBulkMutationStart"
          @paste-end="handleBulkMutationEnd"
          @fill-start="handleBulkMutationStart"
          @fill-end="handleBulkMutationEnd"
        />
      </div>
    </div>
  </div>

  <AlertDialogRoot :open="deleteConfirmOpen" @update:open="handleDeleteConfirmOpenChange">
    <AlertDialogPortal>
      <AlertDialogOverlay class="fixed inset-0 z-50 bg-black/45" />
      <AlertDialogContent class="fixed left-1/2 top-1/2 z-[70] w-[92vw] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-lg border bg-background p-5 shadow-xl">
        <AlertDialogTitle class="text-base font-semibold">{{ t('htFeeDetail.dialog.deleteTitle') }}</AlertDialogTitle>
        <AlertDialogDescription class="mt-2 text-sm text-muted-foreground">
          {{ t('htFeeDetail.dialog.deleteDesc', { name: pendingDeleteRowName }) }}
        </AlertDialogDescription>
        <div class="mt-4 flex items-center justify-end gap-2">
          <AlertDialogCancel as-child>
            <Button variant="outline">{{ t('common.cancel') }}</Button>
          </AlertDialogCancel>
          <AlertDialogAction as-child>
            <Button variant="destructive" @click="confirmDeleteRow">{{ t('common.delete') }}</Button>
          </AlertDialogAction>
        </div>
      </AlertDialogContent>
    </AlertDialogPortal>
  </AlertDialogRoot>
</template>
