<script lang="ts" setup>
import type { CellPosition, ColumnConfig } from './types'
import { Icon } from '@bagelink/vue'
import SpreadsheetCell from './SpreadsheetCell.vue'

interface Props {
	columns: ColumnConfig[]
	rows: Array<{ [key: string]: any }>
	isFixed: boolean
	showRowNumbers: boolean
	columnWidths: Map<string, number>
	sortColumn: string | null
	sortDirection: 'asc' | 'desc'
	selectionStart: CellPosition | null
	selectionEnd: CellPosition | null
	editingCell: CellPosition | null
	baseColumnIndex: number
	wrapText?: boolean
	emptyMessage?: string
}

const props = defineProps<Props>()

defineEmits<{
	(e: 'sortColumn', key: string): void
	(e: 'resizeStart', event: MouseEvent, key: string): void
	(e: 'selectRow', rowIndex: number): void
	(e: 'cellMouseDown', rowIndex: number, colIndex: number): void
	(e: 'cellMouseOver', rowIndex: number, colIndex: number): void
	(e: 'cellEditStart', rowIndex: number, colIndex: number): void
	(e: 'cellKeyDown', event: KeyboardEvent, rowIndex: number, colIndex: number): void
	(e: 'updateCell', rowIndex: number, key: string, value: string | boolean): void
	(e: 'stopEditing', cancelled: boolean): void
	(e: 'setInputRef', el: any, key: string): void
}>()

// Calculate the actual column index including the base index
const getActualColumnIndex = (localIndex: number) => props.baseColumnIndex + localIndex

// Determines if a cell is selected
function isCellSelected(row: number, localColIndex: number): boolean {
	if (!props.selectionStart || !props.selectionEnd) { return false }

	const colIndex = getActualColumnIndex(localColIndex)
	const startRow = Math.min(props.selectionStart.row, props.selectionEnd.row)
	const endRow = Math.max(props.selectionStart.row, props.selectionEnd.row)
	const startCol = Math.min(props.selectionStart.col, props.selectionEnd.col)
	const endCol = Math.max(props.selectionStart.col, props.selectionEnd.col)

	return row >= startRow && row <= endRow && colIndex >= startCol && colIndex <= endCol
}

// Check if a cell is editable
function isCellEditable(columnKey: string): boolean {
	const column = props.columns.find(col => col.key === columnKey)
	return !(column?.locked ?? false)
}

// Check if a cell is currently being edited
function isEditing(row: number, localColIndex: number): boolean {
	if (!props.editingCell) { return false }
	const colIndex = getActualColumnIndex(localColIndex)
	return props.editingCell.row === row && props.editingCell.col === colIndex
}

// Returns a stable key representing this row's selection state.
// Used by v-memo so rows outside the selection don't re-render on selection changes.
function rowSelectionKey(rowIndex: number): string {
	if (!props.selectionStart || !props.selectionEnd) { return 'none' }
	const startRow = Math.min(props.selectionStart.row, props.selectionEnd.row)
	const endRow = Math.max(props.selectionStart.row, props.selectionEnd.row)
	if (rowIndex < startRow || rowIndex > endRow) { return 'none' }
	const startCol = Math.min(props.selectionStart.col, props.selectionEnd.col)
	const endCol = Math.max(props.selectionStart.col, props.selectionEnd.col)
	return `${startCol}-${endCol}`
}

function selectionEdgeClasses(rowIndex: number, localColIndex: number): Record<string, boolean> {
	if (!props.selectionStart || !props.selectionEnd) { return {} }
	const startRow = Math.min(props.selectionStart.row, props.selectionEnd.row)
	const endRow = Math.max(props.selectionStart.row, props.selectionEnd.row)
	const startCol = Math.min(props.selectionStart.col, props.selectionEnd.col)
	const endCol = Math.max(props.selectionStart.col, props.selectionEnd.col)
	const colIndex = getActualColumnIndex(localColIndex)
	if (rowIndex < startRow || rowIndex > endRow || colIndex < startCol || colIndex > endCol) { return {} }
	return {
		'sel-top': rowIndex === startRow,
		'sel-bottom': rowIndex === endRow,
		'sel-left': colIndex === startCol,
		'sel-right': colIndex === endCol,
	}
}
</script>

<template>
	<table :class="{ 'fixed-columns': isFixed, 'w-100p': !isFixed }">
		<thead class=" stickyTop">
			<tr>
				<th v-if="showRowNumbers" class="row-number-header" />
				<th
					v-for="col in columns"
					:key="col.key"
					:style="{ width: columnWidths.get(col.key) ? `${columnWidths.get(col.key)}px` : col.width }"
				>
					<div class="th-content">
						<span @click="col.sortable && $emit('sortColumn', col.key)">
							{{ col.label || col.key }}
						</span>
						<Icon
							v-if="sortColumn === col.key"
							class="line-height-0 transition-400"
							name="keyboard_arrow_down"
							:class="{ 'rotate-180': sortDirection === 'desc' }"
						/>
						<div
							class="resize-handle"
							@mousedown.stop="$emit('resizeStart', $event, col.key)"
						/>
					</div>
				</th>
			</tr>
		</thead>
		<tbody>
			<tr
				v-for="(row, rowIndex) in rows"
				:key="rowIndex"
				v-memo="[rowSelectionKey(rowIndex), editingCell?.row === rowIndex, row, wrapText]"
			>
				<td
					v-if="showRowNumbers"
					class="row-number txt-center hover user-select-none pointer txt12 regular"
					@click="$emit('selectRow', rowIndex)"
				>
					{{ rowIndex + 1 }}
				</td>
				<td
					v-for="(col, colIndex) in columns"
					:key="col.key"
					:class="{
						'selected': isCellSelected(rowIndex, colIndex),
						'locked': !isCellEditable(col.key),
						'wrap-text': wrapText,
						...selectionEdgeClasses(rowIndex, colIndex),
					}"
					:style="{ width: columnWidths.get(col.key) ? `${columnWidths.get(col.key)}px` : col.width }"
					:tabindex="col.hidden ? undefined : 0"
					@mousedown="$emit('cellMouseDown', rowIndex, getActualColumnIndex(colIndex))"
					@mouseover="$emit('cellMouseOver', rowIndex, getActualColumnIndex(colIndex))"
					@focusin="$emit('cellMouseOver', rowIndex, getActualColumnIndex(colIndex))"
					@dblclick="$emit('cellEditStart', rowIndex, getActualColumnIndex(colIndex))"
					@keydown="$emit('cellKeyDown', $event, rowIndex, getActualColumnIndex(colIndex))"
				>
					<SpreadsheetCell
						:col="col"
						:row="row"
						:row-index="rowIndex"
						:global-col-index="getActualColumnIndex(colIndex)"
						:editing="isEditing(rowIndex, colIndex)"
						:editable="isCellEditable(col.key)"
						:wrap-text="wrapText"
						@updateCell="(ri, key, val) => $emit('updateCell', ri, key, val)"
						@stopEditing="(cancelled) => $emit('stopEditing', cancelled)"
						@setInputRef="(el, key) => $emit('setInputRef', el, key)"
					/>
				</td>
			</tr>
			<tr v-if="!isFixed && rows.length === 0">
				<td
					:colspan="columns.length + (showRowNumbers ? 1 : 0)"
					class="empty-state"
				>
					{{ emptyMessage ?? 'No results' }}
				</td>
			</tr>
		</tbody>
	</table>
</template>

<style scoped>

.row-number{
position: sticky;
inset-inline-start: 0;
background: var(--bgl-input-bg);
color: var(--txt-muted, #888);
z-index: 1;
}
.row-number:before{
content: '';
position: absolute;
inset: -1px;
border: 1px solid var(--bgl-border-color);
}

.stickyTop{
position: sticky;
top: 0px;
z-index: 2;

}
.stickyTop:before{
content: '1';
position: absolute;
display: block;
inset: 0;
border-bottom: 1px solid var(--bgl-border-color);
}

.fixed-columns {
border-right: 2px solid var(--bgl-border-color);
}
table {
border-collapse: collapse;
}
th, td {
padding: 0.1rem 0.5rem;
min-width: 80px;
background: var(--bgl-box-bg);
user-select: none;
}

th {
background: var(--bgl-input-bg);
white-space: nowrap;
position: relative;
padding: 0.25rem 0.5rem;
font-weight: 500;
text-align: start;
border-bottom: 1px solid var(--bgl-border-color);
border-inline-start: 1px solid var(--bgl-border-color);

}
th .bgl_icon-font{
vertical-align: middle;
}
td.selected {
background: var(--bgl-primary-light);
}
td.sel-top    { box-shadow: inset 0 2px 0 var(--bgl-primary); }
td.sel-bottom { box-shadow: inset 0 -2px 0 var(--bgl-primary); }
td.sel-left   { box-shadow: inset 2px 0 0 var(--bgl-primary); }
td.sel-right  { box-shadow: inset -2px 0 0 var(--bgl-primary); }
td.sel-top.sel-bottom    { box-shadow: inset 0 2px 0 var(--bgl-primary), inset 0 -2px 0 var(--bgl-primary); }
td.sel-top.sel-left      { box-shadow: inset 2px 2px 0 var(--bgl-primary); }
td.sel-top.sel-right     { box-shadow: inset -2px 2px 0 var(--bgl-primary); }
td.sel-bottom.sel-left   { box-shadow: inset 2px -2px 0 var(--bgl-primary); }
td.sel-bottom.sel-right  { box-shadow: inset -2px -2px 0 var(--bgl-primary); }
td.sel-top.sel-left.sel-right      { box-shadow: inset 2px 2px 0 var(--bgl-primary), inset -2px 2px 0 var(--bgl-primary); }
td.sel-bottom.sel-left.sel-right   { box-shadow: inset 2px -2px 0 var(--bgl-primary), inset -2px -2px 0 var(--bgl-primary); }
td.sel-top.sel-bottom.sel-left     { box-shadow: inset 2px 2px 0 var(--bgl-primary), inset 2px -2px 0 var(--bgl-primary); }
td.sel-top.sel-bottom.sel-right    { box-shadow: inset -2px 2px 0 var(--bgl-primary), inset -2px -2px 0 var(--bgl-primary); }
td.sel-top.sel-bottom.sel-left.sel-right { box-shadow: inset 2px 2px 0 var(--bgl-primary), inset -2px -2px 0 var(--bgl-primary), inset -2px 2px 0 var(--bgl-primary), inset 2px -2px 0 var(--bgl-primary); }
td.locked {
background: var(--bgl-gray-light);
cursor: default;
}
td.locked.selected {
background: var(--bgl-primary-light);
}
td {
border: 1px solid var(--bgl-border-color);
height: 40px;
vertical-align: middle;
}
td:has(img){
padding: 0;
}
td span{
display: block;
display: -webkit-box;
max-width: 100%;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 1;
word-break: break-all;
}
td.wrap-text {
height: auto;
min-height: 40px;
}
td.wrap-text span {
white-space: normal;
-webkit-line-clamp: unset;
word-break: break-word;
}
input {
width: 100%;
border: none;
background: transparent;
padding: 0;
margin: 0;
min-height: 0;
min-width: 0;
}
input:focus {
outline: 2px solid var(--bgl-primary);
outline-offset: 6px;
}
th.sortable {
cursor: pointer;
}
.row-number-header, .row-number {
width: fit-content;
min-width: fit-content !important;
padding: 0.1rem 0.7rem !important;
}
td .bgl-checkbox{
margin: 0;
text-align: center;
justify-content: center;
}
td:has(.bgl-checkbox){
text-align: center;
background: var(--bgl-input-bg);
}
td:has(:checked){
background: var(--bgl-primary-light);
}

.spreadsheetCellPlaceHolder{
height: 0px;
overflow: hidden;
opacity: 0;
pointer-events: none;
user-select: none;
}

.th-content {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 100%;
}

.resize-handle {
position: absolute;
right: -8px;
top: 0;
bottom: 0;
width: 4px;
z-index: 100;
cursor: col-resize;
background: transparent;
transition: background 0.2s;
}

.resize-handle:hover {
background: var(--bgl-primary);
}

.empty-state {
text-align: center;
padding: 2rem 1rem;
color: var(--bgl-label-color, #888);
font-size: 0.875rem;
background: var(--bgl-box-bg);
border: 1px solid var(--bgl-border-color);
}

</style>
