<script setup lang="ts" generic="T extends { [key: string]: any }, P extends Path<T>">
import type { BaseBagelField, Field, Path } from '@bagelink/vue'
import type { SetupContext } from 'vue'
import type { TableSchemaProps } from '../../types/TableSchema'
import {
	Icon,
	keyToLabel,
	Loading
} from '@bagelink/vue'
import { useSlots, watch, computed, toValue } from 'vue'
import { useSchemaField } from '../../composables/useSchemaField'
import { useTableData } from './useTableData'
import { useTableSelection } from './useTableSelection'
import { useTableVirtualization } from './useTableVirtualization'

export type SortDirectionsT = 'ASC' | 'DESC'
export type EmitOrderT = `${string} ${SortDirectionsT}`

const props = defineProps<TableSchemaProps<T>>()

const emit = defineEmits<{
	orderBy: [EmitOrderT]
	select: [T]
	lastItemVisible: []
}>()

const slots: SetupContext['slots'] = useSlots()

const loading = defineModel('loading', { default: false })
const itemHeight = defineModel('itemHeight', { default: 50 })
const selectedItems = defineModel<string[]>('selectedItems', { default: [] as string[] })

const {
	computedSchema,
	computedData,
	sortField,
	sortDirection,
	toggleSort,
	cleanTransformedData
} = useTableData<T>({
	data: computed(() => props.data),
	schema: computed(() => toValue(props.schema)),
	columns: computed(() => toValue(props.columns)),
	useServerSort: computed(() => toValue(props.useServerSort)),
	onSort: (field, direction) => {
		emit('orderBy', `${field.trim()} ${direction.trim()}` as EmitOrderT)
	}
})

const {
	computedSelectedItems,
	isSelectable,
	allSelectorEl,
	updateAllSelectorState,
	toggleSelectItem,
	toggleSelectAll
} = useTableSelection<T>({
	data: computedData,
	selectable: props.selectable,
	selectedItems,
	cleanData: cleanTransformedData,
	onSelect: (item) => { emit('select', item) }
})

const {
	list,
	containerProps,
	wrapperProps,
	scrollTo,
	lastItemEl,
	registerLastItemObserver
} = useTableVirtualization<T>({
	data: computedData,
	itemHeight: itemHeight.value,
	onLastItemVisible: () => {
		emit('lastItemVisible')
		props.onLastItemVisible?.()
	}
})

function renderFieldForRow(field: Field<T>, row: T) {
	const { renderField } = useSchemaField<T>({
		mode: 'table',
		getFormData: () => row,
	})
	return renderField({ ...field, label: '' } as BaseBagelField<T, Path<T>>, slots as unknown as BaseBagelField<T, Path<T>>['slots'])
}

const computedItemHeight = computed(() => `${itemHeight.value}px`)

watch(
	() => loading.value,
	(newLoadingVal, oldLoadingVal) => {
		if (newLoadingVal === oldLoadingVal) { return }
		if (!newLoadingVal) { registerLastItemObserver() }
	},
	{ immediate: true }
)

watch(
	() => computedSelectedItems.value.length,
	() => { updateAllSelectorState() }
)

watch(
	() => computedData.value,
	(newData, oldData) => {
		if (newData.length === oldData.length || props.onLastItemVisible !== undefined) { return }
		scrollTo(0)
	}
)

const showLoading = computed(() => loading.value)
</script>

<template>
	<div class="table-list-wrap h-100p" v-bind="containerProps" :class="{ overflowHiddenLoading: showLoading }">
		<Loading v-if="showLoading" class="h100p" />
		<div v-bind="wrapperProps" :class="{ 'pointer-events-none': showLoading }">
			<table class="infinite-wrapper" :class="{ selecting: computedSelectedItems.length }">
				<thead class="row first-row">
					<th v-if="isSelectable">
						<input ref="allSelectorEl" type="checkbox" @click.stop @change="(e) => toggleSelectAll(e)">
					</th>
					<th
						v-for="field in computedSchema" :key="field.id" class="col"
						@click="toggleSort(field?.id || '')"
					>
						<div class="flex">
							{{ field.label || field.attrs?.label || keyToLabel(field?.id) }}
							<div class="list-arrows" :class="{ sorted: sortField === field?.id }">
								<Icon :class="{ desc: sortDirection === 'DESC' }" icon="keyboard_arrow_up" />
							</div>
						</div>
					</th>
				</thead>
				<tbody>
					<tr
						v-for="{ data: row } in list" :key="row?.id || `row-${Math.random()}`"
						class="row row-item position-relative"
						:class="{ selected: row?.id && computedSelectedItems.includes(row.id) }"
						@click="(event) => toggleSelectItem(row, event)"
					>
						<td v-if="isSelectable">
							<div @click.stop>
								<input v-model="selectedItems" type="checkbox" :value="row?.id || ''">
							</div>
						</td>
						<td
							v-for="field in computedSchema" :key="`${field.id}-${row?.id || Math.random()}`"
							class="col"
						>
							<slot v-if="field.id && slots?.[field.id]" :name="field.id" :row="row" :field="field" />
							<div v-else>
								<component :is="renderFieldForRow(field, row)" />
							</div>
						</td>
					</tr>
					<tr v-if="props.onLastItemVisible !== undefined" ref="lastItemEl" style="height: 1px;" />
				</tbody>
			</table>
		</div>
	</div>
</template>

<style scoped>
.table-list-wrap {
	min-height: 150px;
}

.selected {
	background: var(--bgl-primary-tint);
}

tbody tr.selected:hover {
	background: var(--bgl-primary-light);
}

.list-arrows {
	opacity: 0;
}

.list-arrows .bgl_icon-font {
	transition: all ease-in-out 0.2s;
}

.list-arrows.sorted {
	opacity: 1;
}

.col img {
	height: 35px;
	margin-top: -14px;
	margin-bottom: -14px;
	border-radius: 5px;
	object-fit: cover;
}

.col:has(img) {
	padding-inline-end: 0.5rem;
}

.selecting .col {
	user-select: none;
}

.overflowHiddenLoading {
	overflow: hidden !important;
}

.list-arrows.sorted .desc {
	transform: rotate(180deg);
	display: inline-block;
}

table {
	border-spacing: 0 15px;
	border-collapse: collapse;
	width: 100%;
}

th {
	font-size: 0.8rem;
	color: var(--bgl-black-tint);
	position: sticky;
	top: 0;
	z-index: 2;
	background: var(--bgl-box-bg);
	height: v-bind(computedItemHeight);
	vertical-align: bottom;
	font-weight: 400;
	text-align: start;
}

.embedded-field {
	margin-bottom: -0.2rem;
	margin-top: -0.2rem;
}

.row {
	border-bottom: 1px solid var(--border-color);
	cursor: pointer;
}

.row.first-row {
	font-size: 0.8rem;
	color: var(--bgl-black-tint);
	position: sticky;
	top: 0;
	z-index: 2;
	background: var(--bgl-box-bg);
	vertical-align: bottom;
}

.row.first-row::after {
	content: '';
	border-bottom: 1px solid var(--border-color);
	position: absolute;
	left: 0;
	right: 0;
	bottom: -1px;
}

.first-row .col {
	cursor: pointer;
	background: var(--bgl-box-bg);
}

.col {
	white-space: nowrap;
	padding: 0.75rem 1rem;
	transition: var(--bgl-transition);
	line-height: 1;
	align-items: center;
	text-overflow: ellipsis;
	overflow: hidden;
	max-width: 30vw;
}

.col:has(.bagel-input) {
	padding: 0rem 0.25rem;
}

.col>div {
	display: flex;
	gap: 0.5rem;
}

.max-col-width {
	max-width: 30vw;
	overflow: hidden;
	text-overflow: ellipsis;
}

.col.check .bgl_icon-font {
	border-radius: 100%;
	background: var(--bgl-blue-tint);
	color: var(--bgl-primary);
	width: 20px;
	height: 20px;
	display: flex;
	align-items: center;
	justify-content: center;
	margin-top: -2px;
}

.rows {
	font-size: 0.88em;
}

.table-list {
	height: 100%;
	position: relative;
	padding-left: 0 !important;
	padding-right: 0 !important;
	overflow: auto;
}

.BagelTable .table-list {
	overflow: unset;
}

.row-item {
	height: v-bind(computedItemHeight);
	transition: all 200ms ease;
}

.row-item:hover {
	background: var(--bgl-gray-light);
}

.row-item input[type='checkbox'] {
	margin-top: 0.45rem !important;
	accent-color: var(--bgl-accent-color);
}

.infinite-wrapper {
	overflow-y: auto;
	width: 100%;
}

input[type='checkbox'] {
	margin-top: 0.3rem !important;
	accent-color: var(--bgl-accent-color);
	transform: scale(1.2);
	position: relative;
	cursor: pointer;
	display: flex;
	justify-content: center;
	align-items: center;
	height: 0.85rem;
	width: 0.85rem;
}

input[type='checkbox']::before {
	content: '';
	height: 0.85rem;
	width: 0.85rem;
	background: var(--bgl-primary);
	display: block;
	text-align: center;
	border-radius: 100%;
	opacity: 0;
	transition: all 200ms ease;
	transform: scale(1);
	position: absolute;
}

input[type='checkbox']:hover::before {
	opacity: 0.2;
	transform: scale(2);
}

[lang='he'] [dir='ltr'] {
	text-align: right;
}

th input[type='checkbox'] {
	transform: translateY(0.2rem) scale(1.2);
	accent-color: var(--bgl-accent-color);
}

th::after {
	content: '';
	border-bottom: 1px solid var(--border-color);
	position: absolute;
	left: 0;
	right: 0;
	bottom: -1px;
}

tr {
	border-bottom: 1px solid var(--border-color);
	cursor: pointer;
	align-items: center;
}

td,
th {
	white-space: nowrap;
	padding: 0.75rem 0.65rem;
	transition: var(--bgl-transition);
	line-height: 1;
}

tbody tr {
	font-size: 0.88em;
	height: v-bind(computedItemHeight);
	transition: all 200ms ease;
}

tbody tr:hover {
	background: var(--bgl-gray-light);
}
</style>
