<template>
  <div class="space-y-3">
    <UTable :data="tableData" :columns="tableColumns" class="w-full" :ui="{ td: 'p-2' }">
      <template v-for="col in dataColumns" :key="col.key" #[`${col.key}-cell`]="slotProps">
        <!-- Column with cross-reference -->
        <USelectMenu
          v-if="hasColumnReference(col.colIndex) && !isColumnReadOnly(col.colIndex)"
          :model-value="getCellValueForSelect(slotProps.row as TableRow<TableRowData>, col.key, col.colIndex)"
          :items="getColumnOptions(col.colIndex, (slotProps.row as TableRow<TableRowData>).original)"
          :disabled="disabled"
          :placeholder="$t('applicationForms.formElements.table.selectValue')"
          :multiple="isColumnMultipleAllowed(col.colIndex)"
          class="w-full min-w-32"
          @update:model-value="
            (val: string | string[]) =>
              updateCellValue((slotProps.row as TableRow<TableRowData>).index, col.key, col.colIndex, val)
          "
        />
        <!-- Read-only column -->
        <span v-else-if="isColumnReadOnly(col.colIndex)" class="text-muted px-2 py-1">
          {{ formatCellDisplay(slotProps.row as any, col.key, col.colIndex) }}
        </span>
        <!-- Checkbox column -->
        <div v-else-if="isColumnCheckbox(col.colIndex)" class="flex justify-center">
          <UCheckbox
            :model-value="getCellValueForCheckbox(slotProps.row as TableRow<TableRowData>, col.key)"
            :disabled="disabled"
            @update:model-value="
              (val: boolean | 'indeterminate') =>
                updateCheckboxCell((slotProps.row as TableRow<TableRowData>).index, col.colIndex, val === true)
            "
          />
        </div>
        <!-- Regular text input -->
        <UInput
          v-else
          :model-value="getCellValue(slotProps.row as TableRow<TableRowData>, col.key)"
          :disabled="disabled"
          class="w-full min-w-32"
          @update:model-value="
            (val: string | number) => updateCell((slotProps.row as TableRow<TableRowData>).index, col.key, String(val))
          "
        />
      </template>
      <template v-if="canModifyRows" #actions-cell="{ row }">
        <UButton
          v-if="!disabled"
          icon="i-lucide-trash-2"
          color="error"
          variant="ghost"
          size="xs"
          :aria-label="$t('applicationForms.formElements.table.removeRow')"
          @click="removeRow(row.index)"
        />
      </template>
    </UTable>

    <div v-if="tableData.length === 0" class="text-center text-dimmed py-4">
      {{ $t('applicationForms.formElements.table.noData') }}
    </div>

    <UButton v-if="!disabled && canModifyRows" variant="outline" size="sm" leading-icon="i-lucide-plus" @click="addRow">
      {{ $t('applicationForms.formElements.table.addRow') }}
    </UButton>
  </div>
</template>

<script setup lang="ts">
import type { FormElementDto, FormOptionDto, TableRowPresetDto } from '~~/.api-client'
import type { TableColumn, TableRow } from '@nuxt/ui'
import { useTableCrossReferences } from '~/composables/useTableCrossReferences'

const props = defineProps<{
  formOptions: FormOptionDto[]
  disabled?: boolean
  allFormElements?: FormElementDto[]
  tableRowPreset?: TableRowPresetDto
}>()

const emit = defineEmits<{
  (e: 'update:formOptions', value: FormOptionDto[]): void
}>()

const { getReferencedColumnValues, getConstrainedColumnValues, applyRowPresets } = useTableCrossReferences()

const canModifyRows = computed(() => {
  if (!props.tableRowPreset) return true
  return props.tableRowPreset.canAddRows !== false
})

// Watch for changes in source table and apply row presets reactively
const sourceTableOptions = computed(() => {
  if (!props.tableRowPreset?.sourceTableReference || !props.allFormElements) return null
  const sourceTable = props.allFormElements.find(
    (el) => el.reference === props.tableRowPreset?.sourceTableReference && el.type === 'TABLE'
  )
  return sourceTable?.options
})

watch(
  sourceTableOptions,
  () => {
    if (!sourceTableOptions.value || !props.tableRowPreset || !props.allFormElements) return

    const updatedOptions = applyRowPresets(props.tableRowPreset, props.formOptions, props.allFormElements)

    const hasChanges = updatedOptions.some((opt, idx) => opt.value !== props.formOptions[idx]?.value)
    if (hasChanges) {
      emit('update:formOptions', updatedOptions)
    }
  },
  { immediate: true, deep: true }
)

type CellValue = string | string[] | boolean
type TableRowData = Record<string, CellValue>

interface DataColumn {
  key: string
  colIndex: number
}

const dataColumns = computed<DataColumn[]>(() =>
  props.formOptions.map((_, index) => ({
    key: `col_${index}`,
    colIndex: index
  }))
)

const tableColumns = computed<TableColumn<TableRowData>[]>(() => {
  const columns: TableColumn<TableRowData>[] = props.formOptions.map((option, index) => ({
    accessorKey: `col_${index}`,
    header: option.label || ''
  }))

  // Only show actions column if not disabled AND rows can be modified
  if (!props.disabled && canModifyRows.value) {
    columns.push({
      id: 'actions',
      header: ''
    })
  }

  return columns
})

const tableData = computed<TableRowData[]>(() => {
  if (props.formOptions.length === 0) return []

  const columnData: CellValue[][] = props.formOptions.map((option, colIndex) => {
    try {
      const parsed = JSON.parse(option.value || '[]')
      if (!Array.isArray(parsed)) return []

      // For multi-select columns, each cell value is already an array
      // For checkbox columns, each cell value is a boolean
      // For single-select columns, each cell value is a string
      if (isColumnMultipleAllowed(colIndex)) {
        return parsed.map((val: CellValue) => (Array.isArray(val) ? val : []))
      }
      if (isColumnCheckbox(colIndex)) {
        return parsed.map((val: CellValue) => val === true)
      }
      return parsed
    } catch {
      return []
    }
  })

  const rowCount = Math.max(...columnData.map((col) => col.length), 0)

  const rows: TableRowData[] = []
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
    const row: TableRowData = {}
    props.formOptions.forEach((option, colIndex) => {
      const cellValue = columnData[colIndex]?.[rowIndex]
      if (isColumnMultipleAllowed(colIndex)) {
        row[`col_${colIndex}`] = Array.isArray(cellValue) ? cellValue : []
      } else if (isColumnCheckbox(colIndex)) {
        row[`col_${colIndex}`] = cellValue === true
      } else {
        row[`col_${colIndex}`] = typeof cellValue === 'string' ? cellValue : ''
      }
    })
    rows.push(row)
  }

  return rows
})

function hasColumnReference(colIndex: number): boolean {
  const option = props.formOptions[colIndex]
  return !!option?.columnConfig?.sourceTableReference
}

function isColumnReadOnly(colIndex: number): boolean {
  const option = props.formOptions[colIndex]
  return option?.columnConfig?.isReadOnly === true
}

function isColumnMultipleAllowed(colIndex: number): boolean {
  const option = props.formOptions[colIndex]
  return option?.columnConfig?.isMultipleAllowed === true
}

function isColumnCheckbox(colIndex: number): boolean {
  const option = props.formOptions[colIndex]
  return option?.columnConfig?.isCheckbox === true
}

function getColumnOptions(colIndex: number, currentRowData?: TableRowData): string[] {
  const option = props.formOptions[colIndex]
  if (!option?.columnConfig || !props.allFormElements) {
    return []
  }

  const { columnConfig } = option
  const { rowConstraint } = columnConfig

  // If row constraint is configured, filter values based on current row's key value
  if (rowConstraint?.constraintTableReference && currentRowData) {
    const currentRowAsRecord: Record<string, string> = {}
    for (const [key, value] of Object.entries(currentRowData)) {
      currentRowAsRecord[key] =
        typeof value === 'string' ? value : Array.isArray(value) ? value.join(',') : String(value)
    }

    return getConstrainedColumnValues(
      columnConfig,
      currentRowAsRecord,
      rowConstraint.constraintTableReference,
      rowConstraint.constraintKeyColumnIndex ?? 0,
      rowConstraint.constraintValueColumnIndex ?? 1,
      props.allFormElements,
      rowConstraint.currentRowKeyColumnIndex
    )
  }

  return getReferencedColumnValues(columnConfig, props.allFormElements)
}

function getCellValue(row: TableRow<TableRowData>, columnKey: string): string {
  const value = row.original[columnKey]
  return typeof value === 'string' ? value : ''
}

function getCellValueForSelect(row: TableRow<TableRowData>, columnKey: string, colIndex: number): string | string[] {
  const value = row.original[columnKey]
  if (isColumnMultipleAllowed(colIndex)) {
    return Array.isArray(value) ? value : []
  }
  return typeof value === 'string' ? value : ''
}

function getCellValueForCheckbox(row: TableRow<TableRowData>, columnKey: string): boolean {
  const value = row.original[columnKey]
  return value === true
}

function formatCellDisplay(row: TableRow<TableRowData>, columnKey: string, colIndex: number): string {
  const value = row.original[columnKey]
  if (isColumnMultipleAllowed(colIndex) && Array.isArray(value)) {
    return value.length > 0 ? value.join(', ') : '-'
  }
  return (typeof value === 'string' ? value : '') || '-'
}

function updateCell(rowIndex: number, columnKey: string, value: string) {
  const colIndex = parseInt(columnKey.replace('col_', ''), 10)

  const updatedOptions = props.formOptions.map((option, index) => {
    if (index !== colIndex) return option

    let columnValues: CellValue[]
    try {
      columnValues = JSON.parse(option.value || '[]')
      if (!Array.isArray(columnValues)) columnValues = []
    } catch {
      columnValues = []
    }

    while (columnValues.length <= rowIndex) {
      columnValues.push('')
    }
    columnValues[rowIndex] = value

    return { ...option, value: JSON.stringify(columnValues) }
  })

  emit('update:formOptions', updatedOptions)
}

function updateCellValue(rowIndex: number, columnKey: string, colIndex: number, value: string | string[]) {
  const updatedOptions = props.formOptions.map((option, index) => {
    if (index !== colIndex) return option

    let columnValues: CellValue[]
    try {
      columnValues = JSON.parse(option.value || '[]')
      if (!Array.isArray(columnValues)) columnValues = []
    } catch {
      columnValues = []
    }

    const isMultiple = isColumnMultipleAllowed(colIndex)
    while (columnValues.length <= rowIndex) {
      columnValues.push(isMultiple ? [] : '')
    }
    columnValues[rowIndex] = value

    return { ...option, value: JSON.stringify(columnValues) }
  })

  emit('update:formOptions', updatedOptions)
}

function updateCheckboxCell(rowIndex: number, colIndex: number, value: boolean) {
  const updatedOptions = props.formOptions.map((option, index) => {
    if (index !== colIndex) return option

    let columnValues: CellValue[]
    try {
      columnValues = JSON.parse(option.value || '[]')
      if (!Array.isArray(columnValues)) columnValues = []
    } catch {
      columnValues = []
    }

    while (columnValues.length <= rowIndex) {
      columnValues.push(false)
    }
    columnValues[rowIndex] = value

    return { ...option, value: JSON.stringify(columnValues) }
  })

  emit('update:formOptions', updatedOptions)
}

function addRow() {
  const updatedOptions = props.formOptions.map((option, colIndex) => {
    let columnValues: CellValue[]
    try {
      columnValues = JSON.parse(option.value || '[]')
      if (!Array.isArray(columnValues)) columnValues = []
    } catch {
      columnValues = []
    }

    // For multi-select columns, initialize with empty array
    // For checkbox columns, initialize with false
    // Otherwise empty string
    let emptyValue: CellValue = ''
    if (isColumnMultipleAllowed(colIndex)) {
      emptyValue = []
    } else if (isColumnCheckbox(colIndex)) {
      emptyValue = false
    }
    columnValues.push(emptyValue)

    return { ...option, value: JSON.stringify(columnValues) }
  })

  emit('update:formOptions', updatedOptions)
}

function removeRow(rowIndex: number) {
  const updatedOptions = props.formOptions.map((option) => {
    let columnValues: CellValue[]
    try {
      columnValues = JSON.parse(option.value || '[]')
      if (!Array.isArray(columnValues)) columnValues = []
    } catch {
      columnValues = []
    }

    columnValues.splice(rowIndex, 1)

    return { ...option, value: JSON.stringify(columnValues) }
  })

  emit('update:formOptions', updatedOptions)
}
</script>
