<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import type { CellValueChangedEvent, ColDef } from 'ag-grid-community'
import { AgGridVue } from 'ag-grid-vue3'
import 'ag-grid-community/styles/ag-grid.css'
import 'ag-grid-community/styles/ag-theme-alpine.css'
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from '@/components/ui/table'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { Checkbox } from '@/components/ui/checkbox'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import FieldRenderer from '@/components/fields/FieldRenderer.vue'
import { ListViewConfig, ViewMode, FieldType, FieldConfig } from '@/types/field-types'
import { ChevronDown, ChevronUp, Search, Plus, Download, Trash2, Edit } from 'lucide-vue-next'

interface Props {
  config: ListViewConfig
  data?: any[]
  loading?: boolean
  selectable?: boolean
  baseUrl?: string
  totalCount?: number
  searchSummary?: string
}

const props = withDefaults(defineProps<Props>(), {
  data: () => [],
  loading: false,
  selectable: false,
  baseUrl: '/runtime/objects',
  searchSummary: '',
})

const emit = defineEmits<{
  'row-click': [row: any]
  'row-select': [rows: any[]]
  'create': []
  'edit': [row: any]
  'delete': [rows: any[]]
  'action': [actionId: string, rows: any[]]
  'sort': [field: string, direction: 'asc' | 'desc']
  'search': [query: string]
  'refresh': []
  'page-change': [page: number, pageSize: number]
  'load-more': [page: number, pageSize: number]
  'view-change': [mode: 'list' | 'spreadsheet']
  'cell-edit': [payload: { row: any; field: FieldConfig; newValue: any; oldValue: any }]
}>()

// State
const normalizeId = (id: any) => String(id)
const selectedRowIds = ref<string[]>([])
const searchQuery = ref('')
const sortField = ref<string>('')
const sortDirection = ref<'asc' | 'desc'>('asc')
const currentPage = ref(1)
const bulkAction = ref('delete')
const viewMode = ref<'list' | 'spreadsheet'>('list')

// Computed
const visibleFields = computed(() => 
  props.config.fields.filter(f => f.showOnList !== false)
)

const pageSize = computed(() => props.config.pageSize ?? 10)
const maxFrontendRecords = computed(() => props.config.maxFrontendRecords ?? 500)
const totalRecords = computed(() => 
  (props.totalCount && props.totalCount > 0)
    ? props.totalCount
    : props.data.length
)
const useHybridPagination = computed(() => totalRecords.value > maxFrontendRecords.value)
const totalPages = computed(() => Math.max(1, Math.ceil(totalRecords.value / pageSize.value)))
const loadedPages = computed(() => Math.max(1, Math.ceil(props.data.length / pageSize.value)))
const availablePages = computed(() => {
  if (useHybridPagination.value && props.totalCount && props.data.length < props.totalCount) {
    return loadedPages.value
  }
  return totalPages.value
})
const startIndex = computed(() => (currentPage.value - 1) * pageSize.value)
const paginatedData = computed(() => {
  const start = startIndex.value
  const end = start + pageSize.value
  return props.data.slice(start, end)
})
const pageStart = computed(() => (props.data.length === 0 ? 0 : startIndex.value + 1))
const pageEnd = computed(() => Math.min(startIndex.value + paginatedData.value.length, totalRecords.value))
const showPagination = computed(() => viewMode.value === 'list' && totalRecords.value > pageSize.value)
const canGoPrev = computed(() => currentPage.value > 1)
const canGoNext = computed(() => currentPage.value < availablePages.value)
const showLoadMore = computed(() => (
  useHybridPagination.value &&
  Boolean(props.totalCount) &&
  props.data.length < totalRecords.value
))

const allSelected = computed({
  get: () => props.data.length > 0 && selectedRowIds.value.length === props.data.length,
  set: (val: boolean) => {
    if (val) {
      selectedRowIds.value = props.data.map(row => normalizeId(row.id))
    } else {
      selectedRowIds.value = []
    }
    emit('row-select', getSelectedRows())
  },
})

const getSelectedRows = () => {
  const idSet = new Set(selectedRowIds.value)
  return props.data.filter(row => idSet.has(normalizeId(row.id)))
}

const toggleRowSelection = (rowId: string) => {
  const normalizedId = normalizeId(rowId)
  const nextSelection = new Set(selectedRowIds.value)
  nextSelection.has(normalizedId) ? nextSelection.delete(normalizedId) : nextSelection.add(normalizedId)
  selectedRowIds.value = Array.from(nextSelection)
  emit('row-select', getSelectedRows())
}

const setRowSelection = (rowId: string, checked: boolean) => {
  const normalizedId = normalizeId(rowId)
  const nextSelection = new Set(selectedRowIds.value)
  if (checked) {
    nextSelection.add(normalizedId)
  } else {
    nextSelection.delete(normalizedId)
  }
  selectedRowIds.value = Array.from(nextSelection)
  emit('row-select', getSelectedRows())
}

const handleSort = (field: string) => {
  if (sortField.value === field) {
    sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'
  } else {
    sortField.value = field
    sortDirection.value = 'asc'
  }
  emit('sort', field, sortDirection.value)
}

const handleSearch = () => {
  emit('search', searchQuery.value)
}

const handleAction = (actionId: string) => {
  emit('action', actionId, getSelectedRows())
}

const handleBulkAction = () => {
  if (bulkAction.value === 'delete') {
    emit('delete', getSelectedRows())
    return
  }
  emit('action', bulkAction.value, getSelectedRows())
}

const isEditableField = (field: FieldConfig) =>
  field.showOnEdit !== false &&
  !field.isReadOnly &&
  ![FieldType.BELONGS_TO, FieldType.HAS_MANY, FieldType.MANY_TO_MANY].includes(field.type)

const getRelationPropertyName = (apiName: string) => apiName.replace(/Id$/, '').toLowerCase()

const formatFieldValue = (field: FieldConfig, value: any, record?: any) => {
  if (value === null || value === undefined) return ''
  switch (field.type) {
    case FieldType.BELONGS_TO: {
      const relationPropertyName = getRelationPropertyName(field.apiName)
      const relatedObject = record?.[relationPropertyName]
      if (relatedObject && typeof relatedObject === 'object') {
        const displayField = field.relationDisplayField || 'name'
        return relatedObject[displayField] || relatedObject.id || value
      }
      return value
    }
    case FieldType.DATE: {
      const date = value instanceof Date ? value : new Date(value)
      return Number.isNaN(date.getTime())
        ? String(value)
        : date.toLocaleDateString(undefined, { year: 'numeric', month: '2-digit', day: '2-digit' })
    }
    case FieldType.DATETIME: {
      const date = value instanceof Date ? value : new Date(value)
      return Number.isNaN(date.getTime())
        ? String(value)
        : date.toLocaleString(undefined, {
            year: 'numeric',
            month: '2-digit',
            day: '2-digit',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false,
          })
    }
    case FieldType.BOOLEAN:
      return value ? 'Yes' : 'No'
    case FieldType.CURRENCY:
      return `${field.prefix || '$'}${Number(value).toFixed(2)}${field.suffix || ''}`
    case FieldType.SELECT: {
      const option = field.options?.find(opt => opt.value === value)
      return option?.label || value
    }
    case FieldType.MULTI_SELECT:
      return Array.isArray(value)
        ? value
            .map(v => field.options?.find(opt => opt.value === v)?.label || v)
            .join(', ')
        : ''
    default:
      return String(value)
  }
}

const columnDefs = computed<ColDef[]>(() =>
  visibleFields.value.map(field => ({
    field: field.apiName,
    headerName: field.label,
    sortable: field.sortable !== false,
    editable: isEditableField(field),
    valueFormatter: params => formatFieldValue(field, params.value, params.data),
    cellEditor:
      field.type === FieldType.SELECT
        ? 'agSelectCellEditor'
        : field.type === FieldType.MULTI_SELECT
          ? 'agTextCellEditor'
          : field.type === FieldType.NUMBER || field.type === FieldType.CURRENCY
            ? 'agTextCellEditor'
            : undefined,
    cellEditorParams:
      field.type === FieldType.SELECT
        ? { values: (field.options || []).map(opt => opt.value) }
        : undefined,
    valueParser:
      field.type === FieldType.NUMBER || field.type === FieldType.CURRENCY
        ? params => (params.newValue === '' ? null : Number(params.newValue))
        : undefined,
    flex: 1,
    minWidth: 160,
  }))
)

const defaultColDef: ColDef = {
  resizable: true,
  sortable: true,
}

const handleCellValueChanged = (event: CellValueChangedEvent) => {
  const field = visibleFields.value.find(item => item.apiName === event.colDef.field)
  if (!field || event.newValue === event.oldValue) return
  emit('cell-edit', {
    row: event.data,
    field,
    newValue: event.newValue,
    oldValue: event.oldValue,
  })
}

const goToPage = (page: number) => {
  const nextPage = Math.min(Math.max(page, 1), availablePages.value)
  if (nextPage !== currentPage.value) {
    currentPage.value = nextPage
    emit('page-change', nextPage, pageSize.value)
  }
}

const loadMore = () => {
  const nextPage = Math.ceil(props.data.length / pageSize.value) + 1
  emit('load-more', nextPage, pageSize.value)
}

watch(
  () => [props.data.length, totalRecords.value, pageSize.value],
  () => {
    if (currentPage.value > availablePages.value) {
      currentPage.value = availablePages.value
    }
  }
)

watch(
  () => props.data,
  (rows) => {
    const rowIds = new Set(rows.map(row => normalizeId(row.id)))
    const nextSelection = selectedRowIds.value.filter(id => rowIds.has(id))
    if (nextSelection.length !== selectedRowIds.value.length) {
      selectedRowIds.value = nextSelection
      emit('row-select', getSelectedRows())
    }
  },
  { deep: true }
)

watch(
  () => viewMode.value,
  (mode) => {
    emit('view-change', mode)
  }
)
</script>

<template>
  <div class="list-view space-y-4">
    <!-- Toolbar -->
    <div class="flex items-center justify-between gap-4">
      <!-- Search -->
      <div v-if="config.searchable !== false" class="flex-1 max-w-sm">
        <div class="relative">
          <Search class="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
          <Input
            v-model="searchQuery"
            placeholder="Search..."
            class="pl-8"
            @keyup.enter="handleSearch"
          />
        </div>
        <p v-if="searchSummary" class="mt-2 text-xs text-muted-foreground">
          {{ searchSummary }}
        </p>
      </div>

      <div class="flex items-center gap-2">
        <Select v-model="viewMode">
          <SelectTrigger class="h-8 w-[180px]">
            <SelectValue placeholder="Select view" />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="list">List view</SelectItem>
            <SelectItem value="spreadsheet">Spreadsheet view</SelectItem>
          </SelectContent>
        </Select>
        <!-- Bulk Actions -->
        <template v-if="selectedRowIds.length > 0">
          <Badge variant="secondary" class="px-3 py-1">
            {{ selectedRowIds.length }} selected
          </Badge>
          <div class="flex items-center gap-2">
            <Select v-model="bulkAction" @update:model-value="(value) => bulkAction = value">
              <SelectTrigger class="h-8 w-[180px]">
                <SelectValue placeholder="Select action" />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="delete">Delete selected</SelectItem>
              </SelectContent>
            </Select>
            <Button variant="outline" size="sm" @click="handleBulkAction">
              <Trash2 class="h-4 w-4 mr-2" />
              Run
            </Button>
          </div>
        </template>

        <!-- Custom Actions -->
        <Button
          v-for="action in config.actions"
          :key="action.id"
          :variant="action.variant || 'outline'"
          size="sm"
          @click="handleAction(action.id)"
        >
          {{ action.label }}
        </Button>

        <!-- Export -->
        <Button v-if="config.exportable" variant="outline" size="sm">
          <Download class="h-4 w-4 mr-2" />
          Export
        </Button>

        <!-- Create -->
        <Button size="sm" @click="emit('create')">
          <Plus class="h-4 w-4 mr-2" />
          New
        </Button>
      </div>
    </div>

    <!-- Table -->
    <div class="border rounded-lg">
      <Table v-if="viewMode === 'list'">
        <TableHeader>
          <TableRow>
            <TableHead v-if="selectable" class="w-12">
              <Checkbox
                :model-value="allSelected"
                @update:model-value="(value: boolean) => (allSelected = value)"
              />
            </TableHead>
            <TableHead
              v-for="field in visibleFields"
              :key="field.id"
              :class="{ 'cursor-pointer hover:bg-muted/50': field.sortable !== false }"
              @click="field.sortable !== false && handleSort(field.apiName)"
            >
              <div class="flex items-center gap-2">
                {{ field.label }}
                <template v-if="field.sortable !== false && sortField === field.apiName">
                  <ChevronUp v-if="sortDirection === 'asc'" class="h-4 w-4" />
                  <ChevronDown v-else class="h-4 w-4" />
                </template>
              </div>
            </TableHead>
            <TableHead class="w-20">Actions</TableHead>
          </TableRow>
        </TableHeader>
        <TableBody>
          <TableRow v-if="loading">
            <TableCell :colspan="visibleFields.length + (selectable ? 2 : 1)" class="text-center py-8">
              <div class="flex items-center justify-center">
                <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
              </div>
            </TableCell>
          </TableRow>
          <TableRow v-else-if="data.length === 0">
            <TableCell :colspan="visibleFields.length + (selectable ? 2 : 1)" class="text-center py-8 text-muted-foreground">
              No records found
            </TableCell>
          </TableRow>
          <TableRow
            v-else
            v-for="row in paginatedData"
            :key="row.id"
            class="cursor-pointer hover:bg-muted/50"
            @click="emit('row-click', row)"
          >
            <TableCell v-if="selectable" @click.stop>
              <Checkbox
                :model-value="selectedRowIds.includes(normalizeId(row.id))"
                @update:model-value="(checked: boolean) => setRowSelection(normalizeId(row.id), checked)"
              />
            </TableCell>
            <TableCell v-for="field in visibleFields" :key="field.id">
              <FieldRenderer
                :field="field"
                :model-value="row[field.apiName]"
                :record-data="row"
                :mode="ViewMode.LIST"
                :base-url="baseUrl"
              />
            </TableCell>
            <TableCell @click.stop>
              <div class="flex items-center gap-1">
                <Button variant="ghost" size="sm" @click="emit('edit', row)">
                  <Edit class="h-4 w-4" />
                </Button>
                <Button variant="ghost" size="sm" @click="emit('delete', [row])">
                  <Trash2 class="h-4 w-4" />
                </Button>
              </div>
            </TableCell>
          </TableRow>
        </TableBody>
      </Table>
      <div v-else class="ag-theme-alpine">
        <AgGridVue
          :row-data="data"
          :column-defs="columnDefs"
          :default-col-def="defaultColDef"
          dom-layout="autoHeight"
          row-selection="multiple"
          suppress-row-click-selection
          single-click-edit
          @cell-value-changed="handleCellValueChanged"
        />
      </div>
    </div>

    <div v-if="showPagination" class="flex flex-wrap items-center justify-between gap-3 text-sm text-muted-foreground">
      <div class="flex items-center gap-2">
        <span>Showing {{ pageStart }}-{{ pageEnd }} of {{ totalRecords }} records</span>
        <span v-if="showLoadMore">
          (loaded {{ data.length }})
        </span>
      </div>
      <div class="flex flex-wrap items-center gap-2">
        <Button variant="outline" size="sm" :disabled="!canGoPrev" @click="goToPage(currentPage - 1)">
          Previous
        </Button>
        <span class="px-2">Page {{ currentPage }} of {{ totalPages }}</span>
        <Button variant="outline" size="sm" :disabled="!canGoNext" @click="goToPage(currentPage + 1)">
          Next
        </Button>
        <Button v-if="showLoadMore" variant="secondary" size="sm" @click="loadMore">
          Load more
        </Button>
      </div>
    </div>
  </div>
</template>

<style scoped>
.list-view {
  width: 100%;
}

.list-view :deep(.border) {
  background-color: hsl(var(--card));
}

.list-view :deep(input) {
  background-color: hsl(var(--background));
}
</style>
