<template>
  <div class="space-y-3">
    <div class="flex justify-end">
      <UTooltip :text="$t('applicationForms.formElements.table.enlargeTable')">
        <UButton
          icon="i-lucide-maximize-2"
          color="neutral"
          variant="ghost"
          size="xs"
          :aria-label="$t('applicationForms.formElements.table.enlargeTable')"
          @click="fullscreenOpen = true"
        />
      </UTooltip>
    </div>

    <!-- Regular table -->
    <TheTableContent
      :table-data="tableData"
      :table-columns="tableColumns"
      :data-columns="dataColumns"
      :form-options="formOptions"
      :disabled="disabled"
      :can-modify-rows="canModifyRows"
      :get-column-options="getColumnOptions"
      :read-only-column-indices="readOnlyColumnIndices"
      :is-cell-visible="isCellVisible"
      @update:cell="updateCell"
      @update:cell-value="updateCellValue"
      @update:checkbox-cell="updateCheckboxCell"
      @add-row="addRow"
      @remove-row="removeRow"
    />

    <!-- Fullscreen Modal -->
    <UModal
      v-model:open="fullscreenOpen"
      :title="$t('applicationForms.formElements.table.enlargeTable')"
      class="min-w-3/4"
    >
      <template #content>
        <div class="flex flex-col h-full">
          <!-- Modal header -->
          <div class="flex items-center justify-between p-4 border-b border-default">
            <h2 class="text-lg font-semibold">{{ $t('applicationForms.formElements.table.editTable') }}</h2>
            <UButton
              icon="i-lucide-x"
              color="neutral"
              variant="ghost"
              size="sm"
              :aria-label="$t('common.close')"
              @click="fullscreenOpen = false"
            />
          </div>

          <!-- Modal body with full-width table -->
          <div class="flex-1 overflow-auto p-4">
            <TheTableContent
              :table-data="tableData"
              :table-columns="tableColumns"
              :data-columns="dataColumns"
              :form-options="formOptions"
              :disabled="disabled"
              :can-modify-rows="canModifyRows"
              :get-column-options="getColumnOptions"
              :read-only-column-indices="readOnlyColumnIndices"
              :is-cell-visible="isCellVisible"
              add-row-button-class="mt-4"
              @update:cell="updateCell"
              @update:cell-value="updateCellValue"
              @update:checkbox-cell="updateCheckboxCell"
              @add-row="addRow"
              @remove-row="removeRow"
            />
          </div>
        </div>
      </template>
    </UModal>
  </div>
</template>

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

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

const { isFormOptionVisible } = useFormElementVisibility()

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

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

const fullscreenOpen = ref(false)

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
}

// Filter columns based on visibility conditions
interface VisibleColumn {
  option: FormOptionDto
  originalIndex: number
}

const visibleColumns = computed<VisibleColumn[]>(() => {
  return props.formOptions
    .map((option, index) => ({ option, originalIndex: index }))
    .filter(({ option }) => {
      if (!option.visibilityConditions || !props.allFormElements) {
        return true
      }
      return isFormOptionVisible(option.visibilityConditions, props.allFormElements)
    })
})

const readOnlyColumnIndices = computed<Set<number>>(() => {
  if (!props.allFormElements) return new Set()

  return new Set(
    props.formOptions
      .map((option, index) => ({ option, index }))
      .filter(({ option }) => {
        const conditions = option.columnConfig?.readOnlyConditions
        return conditions && isFormOptionVisible(conditions, props.allFormElements!)
      })
      .map(({ index }) => index)
  )
})

// When columns become read-only, reset their values to the configured default
watch(
  readOnlyColumnIndices,
  (currentSet, previousSet) => {
    const newlyReadOnlyIndices = [...currentSet].filter((i) => !previousSet?.has(i))
    if (newlyReadOnlyIndices.length === 0) return

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

      const columnValues = parseColumnValues(option.value)
      const defaultValue = isColumnCheckbox(colIndex) ? false : (option.columnConfig?.readOnlyDefaultValue ?? '')
      const newValue = JSON.stringify(columnValues.map(() => defaultValue))

      return newValue !== option.value ? { ...option, value: newValue } : option
    })

    if (updatedOptions.some((opt, i) => opt !== props.formOptions[i])) {
      emit('update:formOptions', updatedOptions)
    }
  },
  { immediate: true }
)

const dataColumns = computed<DataColumn[]>(() =>
  visibleColumns.value.map(({ originalIndex }) => ({
    key: `col_${originalIndex}`,
    colIndex: originalIndex
  }))
)

const tableColumns = computed<TableColumn<TableRowData>[]>(() => {
  const columns: TableColumn<TableRowData>[] = visibleColumns.value.map(({ option, originalIndex }) => ({
    accessorKey: `col_${originalIndex}`,
    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) => {
    const parsed = parseColumnValues(option.value)

    // Normalize cell values based on column type
    if (isColumnMultipleAllowed(colIndex)) {
      return parsed.map((val) => (Array.isArray(val) ? val : []))
    }
    if (isColumnCheckbox(colIndex)) {
      return parsed.map((val) => val === true)
    }
    return parsed
  })

  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((_, 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 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 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

    const columnValues = parseColumnValues(option.value)
    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

    const columnValues = parseColumnValues(option.value)
    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

    const columnValues = parseColumnValues(option.value)
    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) => {
    const columnValues = parseColumnValues(option.value)

    // Determine initial value based on column type
    let initialValue: CellValue = ''
    if (readOnlyColumnIndices.value.has(colIndex)) {
      initialValue = isColumnCheckbox(colIndex) ? false : (option.columnConfig?.readOnlyDefaultValue ?? '')
    } else if (isColumnMultipleAllowed(colIndex)) {
      initialValue = []
    } else if (isColumnCheckbox(colIndex)) {
      initialValue = false
    }
    columnValues.push(initialValue)

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

  emit('update:formOptions', updatedOptions)
}

function removeRow(rowIndex: number) {
  const updatedOptions = props.formOptions.map((option) => {
    const columnValues = parseColumnValues(option.value)
    columnValues.splice(rowIndex, 1)
    return { ...option, value: JSON.stringify(columnValues) }
  })

  emit('update:formOptions', updatedOptions)
}

function isCellVisible(colIndex: number, rowData: TableRowData): boolean {
  const option = props.formOptions[colIndex]
  const rowVisibility = option?.columnConfig?.rowVisibilityCondition
  if (!rowVisibility) return true

  const { sourceColumnIndex, expectedValues, operator } = rowVisibility
  const sourceKey = `col_${sourceColumnIndex}`
  const cellValue = rowData[sourceKey]

  let sourceValues: string[] = []
  if (Array.isArray(cellValue)) {
    sourceValues = cellValue
  } else if (typeof cellValue === 'string' && cellValue) {
    sourceValues = cellValue.split(',').map((v) => v.trim())
  }

  if (operator === 'CONTAINS' || operator === 'EQUALS') {
    return (expectedValues ?? []).some((expected) =>
      sourceValues.some((v) => v.toLowerCase() === expected.toLowerCase())
    )
  }
  return true
}

function parseColumnValues(value: string | undefined): CellValue[] {
  try {
    const parsed = JSON.parse(value || '[]')
    return Array.isArray(parsed) ? parsed : []
  } catch {
    return []
  }
}
</script>
