<template>
	<div class="admin-data-table">
		<div v-if="exportable" class="clearfix admin-data-table__toolbar">
			<div class="btn-group pull-right">
				<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown"
					aria-haspopup="true" aria-expanded="false">
					<i class="fa fa-download"></i> Eksport <span class="caret"></span>
				</button>
				<ul class="dropdown-menu dropdown-menu-right">
					<li><a href="#" @click.prevent="exportToCSV">Eksport CSV</a></li>
					<li><a href="#" @click.prevent="exportToExcel">Eksport Excel</a></li>
					<li><a href="#" @click.prevent="exportToPDF">Eksport PDF</a></li>
					<li><a href="#" @click.prevent="exportToJSON">Eksport JSON</a></li>
				</ul>
			</div>
		</div>
		<div class="admin-data-table__shell">
			<div class="admin-data-table__scroll">
				<table class="admin-data-table__table table table-hover">
					<thead>
						<tr>
							<th v-for="h in tableHeaders" :key="h.key" :class="{
								'is-sortable': h.sortable,
								'admin-data-table__col-index': h.key === '__adt_index'
							}" @click="h.sortable && toggleSort(h.key)">
								<div class="admin-data-table__th-inner">
									<span class="admin-data-table__th-title">{{ h.title }}</span>
									<span v-if="h.sortable" class="admin-data-table__sort" aria-hidden="true">
										<template v-if="sortKey === h.key">
											<svg v-if="sortDir === 'asc'" class="admin-data-table__sort-icon"
												xmlns="http://www.w3.org/2000/svg" width="14" height="14"
												viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
												stroke-linecap="round" stroke-linejoin="round">
												<path d="M18 15l-6-6-6 6" />
											</svg>
											<svg v-else class="admin-data-table__sort-icon"
												xmlns="http://www.w3.org/2000/svg" width="14" height="14"
												viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
												stroke-linecap="round" stroke-linejoin="round">
												<path d="M6 9l6 6 6-6" />
											</svg>
										</template>
										<span v-else class="admin-data-table__sort-hint">
											<svg class="admin-data-table__sort-icon admin-data-table__sort-icon--faint"
												xmlns="http://www.w3.org/2000/svg" width="10" height="10"
												viewBox="0 0 24 24" fill="none" stroke="currentColor"
												stroke-width="2.5">
												<path d="M18 15l-6-6-6 6" />
											</svg>
											<svg class="admin-data-table__sort-icon admin-data-table__sort-icon--faint"
												xmlns="http://www.w3.org/2000/svg" width="10" height="10"
												viewBox="0 0 24 24" fill="none" stroke="currentColor"
												stroke-width="2.5">
												<path d="M6 9l6 6 6-6" />
											</svg>
										</span>
									</span>
								</div>
							</th>
						</tr>
					</thead>
					<tbody>
						<tr v-if="loading" class="admin-data-table__state-row">
							<td :colspan="tableHeaders.length">
								<div class="admin-data-table__state">
									<i class="fa fa-refresh fa-spin admin-data-table__state-icon"></i>
									<div class="admin-data-table__state-title">Memuatkan…</div>
								</div>
							</td>
						</tr>
						<tr v-else-if="!displayRows.length" class="admin-data-table__state-row">
							<td :colspan="tableHeaders.length">
								<div class="admin-data-table__state">
									<i class="fa fa-inbox admin-data-table__state-icon"></i>
									<div class="admin-data-table__state-title">{{ emptyText }}</div>
									<div class="admin-data-table__state-hint">Tiada rekod untuk dipaparkan</div>
								</div>
							</td>
						</tr>
						<tr v-else v-for="(item, rowIdx) in displayRows" :key="String(item[itemKey])">
							<td v-for="h in tableHeaders" :key="h.key" :class="cellTdClass(h)">
								<template v-if="h.key === '__adt_index'">
									{{ rowGlobalIndex(rowIdx) }}
								</template>
								<template v-else>
									<slot :name="'item-' + h.key" :item="item" :value="cellValue(item, h.key)">
										{{ formatCell(item, h.key) }}
									</slot>
								</template>
							</td>
						</tr>
					</tbody>
				</table>
			</div>

			<div v-if="showPagination && !loading && totalItems > 0" class="admin-data-table__footer">
				<div class="admin-data-table__footer-left">
					<label class="admin-data-table__footer-label">Baris setiap halaman</label>
					<select v-model.number="localPerPage" class="form-control input-sm admin-data-table__page-size"
						@change="onPerPageChange">
						<option v-for="n in perPageOptions" :key="n" :value="n">{{ n }}</option>
					</select>
				</div>
				<div class="admin-data-table__footer-mid">
					{{ rangeFrom }}–{{ rangeTo }} daripada {{ totalItems }}
				</div>
				<nav class="admin-data-table__footer-right" aria-label="Pagination">
					<ul class="pagination pagination-sm admin-data-table__pagination">
						<li :class="{ disabled: page <= 1 }">
							<a href="#" @click.prevent="goPage(page - 1)">Sebelum</a>
						</li>
						<li :class="{ disabled: page >= totalPages }">
							<a href="#" @click.prevent="goPage(page + 1)">Seterusnya</a>
						</li>
					</ul>
				</nav>
			</div>
		</div>
	</div>
</template>

<script>
import { jsPDF } from 'jspdf';
import autoTable from 'jspdf-autotable';
import * as XLSX from 'xlsx';
import { saveAs } from 'file-saver';

export default {
	name: 'AdminDataTable',

	props: {
		headers: {
			type: Array,
			default: function () {
				return [];
			}
		},
		items: {
			type: Array,
			default: function () {
				return [];
			}
		},
		itemsPerPage: {
			type: Number,
			default: 10
		},
		showPagination: {
			type: Boolean,
			default: true
		},
		loading: {
			type: Boolean,
			default: false
		},
		itemKey: {
			type: String,
			default: 'id'
		},
		emptyText: {
			type: String,
			default: 'Tiada data'
		},
		exportable: {
			type: Boolean,
			default: false
		},
		exportFileName: {
			type: String,
			default: 'table-data'
		},
		showIndex: {
			type: Boolean,
			default: true
		},
		indexTitle: {
			type: String,
			default: 'Bil'
		}
	},

	data: function () {
		return {
			page: 1,
			localPerPage: this.itemsPerPage,
			sortKey: null,
			sortDir: 'asc',
			perPageOptions: [10, 25, 50, 100, 500, 1000]
		};
	},

	computed: {
		totalItems: function () {
			return this.sortedItems.length;
		},
		totalPages: function () {
			var n = Math.ceil(this.totalItems / this.localPerPage) || 1;
			return n;
		},
		sortedItems: function () {
			var list = this.items.slice();
			if (!this.sortKey) {
				return list;
			}
			var key = this.sortKey;
			var dir = this.sortDir === 'desc' ? -1 : 1;
			list.sort(function (a, b) {
				var va = a[key];
				var vb = b[key];
				if (va == null && vb == null) return 0;
				if (va == null) return 1;
				if (vb == null) return -1;
				if (typeof va === 'number' && typeof vb === 'number') {
					return (va - vb) * dir;
				}
				return String(va).localeCompare(String(vb), undefined, { numeric: true }) * dir;
			});
			return list;
		},
		displayRows: function () {
			if (!this.showPagination) {
				return this.sortedItems;
			}
			var start = (this.page - 1) * this.localPerPage;
			return this.sortedItems.slice(start, start + this.localPerPage);
		},
		rangeFrom: function () {
			if (!this.totalItems) return 0;
			return (this.page - 1) * this.localPerPage + 1;
		},
		rangeTo: function () {
			return Math.min(this.page * this.localPerPage, this.totalItems);
		},
		tableHeaders: function () {
			if (!this.showIndex) {
				return this.headers;
			}
			return [
				{ title: this.indexTitle, key: '__adt_index', sortable: false }
			].concat(this.headers);
		}
	},

	watch: {
		items: function () {
			if (this.page > this.totalPages) {
				this.page = Math.max(1, this.totalPages);
			}
		},
		itemsPerPage: function (v) {
			this.localPerPage = v;
		}
	},

	methods: {
		rowGlobalIndex: function (rowIdx) {
			if (!this.showPagination) {
				return rowIdx + 1;
			}
			return (this.page - 1) * this.localPerPage + rowIdx + 1;
		},
		cellTdClass: function (h) {
			var o = {};
			if (h.key === '__adt_index') o['admin-data-table__col-index'] = true;
			return o;
		},
		cellValue: function (item, key) {
			return item[key];
		},
		formatCell: function (item, key) {
			var v = item[key];
			return v == null ? '' : v;
		},
		toggleSort: function (key) {
			if (this.sortKey === key) {
				this.sortDir = this.sortDir === 'asc' ? 'desc' : 'asc';
			} else {
				this.sortKey = key;
				this.sortDir = 'asc';
			}
		},
		goPage: function (p) {
			if (p < 1 || p > this.totalPages) return;
			this.page = p;
		},
		onPerPageChange: function () {
			this.page = 1;
		},

		isActionColumnKey: function (key) {
			if (!key) return false;
			var k = String(key).toLowerCase();
			return k === 'actions' || k === 'action' || k === 'opsi' || k === 'tindakan';
		},

		resolveFieldValue: function (item, headerKey) {
			if (headerKey === '#') return '';
			var keys = String(headerKey).split('.');
			var value = item;
			for (var i = 0; i < keys.length; i++) {
				if (value && typeof value === 'object' && keys[i] in value) {
					value = value[keys[i]];
				} else {
					return null;
				}
			}
			if (Array.isArray(value)) {
				return value
					.map(function (v) {
						return v && typeof v === 'object' && v.name != null ? v.name : String(v);
					})
					.join(', ');
			}
			if (value && typeof value === 'object' && 'name' in value) {
				return value.name;
			}
			return value;
		},

		cellStringForExport: function (header, item, rowIndex) {
			if (typeof header.exportValue === 'function') {
				return header.exportValue(item, rowIndex) || '';
			}
			if (header.key === '__adt_index' || header.key === '#') {
				return String(rowIndex + 1);
			}
			if (this.isActionColumnKey(header.key)) {
				return '';
			}
			var raw = this.resolveFieldValue(item, header.key);
			if (raw == null) return '';
			return String(raw);
		},

		prepareExportMatrix: function () {
			var rows = [];
			var headerTitles = this.tableHeaders.map(function (h) {
				return h.title || h.key;
			});
			var vm = this;
			this.sortedItems.forEach(function (item, index) {
				var line = vm.tableHeaders.map(function (h) {
					return vm.cellStringForExport(h, item, index);
				});
				rows.push(line);
			});
			return { headerTitles: headerTitles, rows: rows };
		},

		exportEmitError: function (err) {
			this.$emit('export-error', err instanceof Error ? err : new Error(String(err)));
		},

		exportToCSV: function () {
			try {
				var m = this.prepareExportMatrix();
				var csvContent = [m.headerTitles]
					.concat(m.rows)
					.map(function (row) {
						return row
							.map(function (cell) {
								return '"' + String(cell).replace(/"/g, '""') + '"';
							})
							.join(',');
					})
					.join('\n');
				var blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
				saveAs(blob, this.exportFileName + '.csv');
			} catch (e) {
				this.exportEmitError(e);
			}
		},

		exportToExcel: function () {
			try {
				var m = this.prepareExportMatrix();
				var aoa = [m.headerTitles].concat(m.rows);
				var worksheet = XLSX.utils.aoa_to_sheet(aoa);
				var workbook = XLSX.utils.book_new();
				XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
				var buf = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
				var blob = new Blob([buf], {
					type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
				});
				saveAs(blob, this.exportFileName + '.xlsx');
			} catch (e) {
				this.exportEmitError(e);
			}
		},

		exportToPDF: function () {
			try {
				var m = this.prepareExportMatrix();
				var colCount = m.headerTitles.length;
				var useLandscape = colCount > 6;
				var doc = new jsPDF(useLandscape ? 'l' : 'p', 'pt', 'a4');
				var fontSize = 8;
				if (colCount > 12) fontSize = 6;
				else if (colCount > 8) fontSize = 7;
				var cellPadding = colCount > 8 ? 4 : 8;
				var tableOptions = {
					head: [m.headerTitles],
					body: m.rows,
					styles: { fontSize: fontSize, cellPadding: cellPadding },
					headStyles: { fillColor: [25, 118, 210] },
					margin: { top: 20 }
				};
				if (colCount > 8) {
					tableOptions.tableWidth = 'wrap';
					tableOptions.horizontalPageBreak = true;
					tableOptions.styles = Object.assign({}, tableOptions.styles, {
						minCellWidth: 36,
						overflow: 'linebreak'
					});
				}
				autoTable(doc, tableOptions);
				doc.save(this.exportFileName + '.pdf');
			} catch (e) {
				this.exportEmitError(e);
			}
		},

		exportToJSON: function () {
			try {
				var vm = this;
				var jsonData = this.sortedItems.map(function (item, index) {
					var obj = {};
					vm.tableHeaders.forEach(function (h) {
						var title = h.title || h.key;
						if (vm.isActionColumnKey(h.key)) return;
						if (h.key === '__adt_index') {
							obj[title] = index + 1;
							return;
						}
						if (typeof h.exportValue === 'function') {
							obj[title] = h.exportValue(item, index);
							return;
						}
						var keys = String(h.key).split('.');
						var value = item;
						for (var i = 0; i < keys.length; i++) {
							if (value && typeof value === 'object' && keys[i] in value) {
								value = value[keys[i]];
							} else {
								value = null;
								break;
							}
						}
						obj[title] = value;
					});
					return obj;
				});
				var jsonContent = JSON.stringify(jsonData, null, 2);
				var blob = new Blob([jsonContent], { type: 'application/json' });
				saveAs(blob, this.exportFileName + '.json');
			} catch (e) {
				this.exportEmitError(e);
			}
		}
	}
};
</script>

<style scoped>

.admin-data-table__toolbar {
	margin-bottom: 12px;
}

.admin-data-table {
	--adt-header-bg: var(--admin-table-header-bg);
	--adt-header-color: var(--admin-table-header-color);
	--adt-radius: 12px;
	--adt-border: 1px solid var(--admin-table-border-color);
	--adt-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
	--adt-row-hover: var(--admin-table-row-hover-bg);
	--adt-footer-bg: var(--admin-table-footer-bg);
	--adt-text-muted: var(--admin-table-text-muted);
	--adt-font-size: 16px;
	--adt-header-font-size: 16px;
}

.admin-data-table__shell {
	border-radius: var(--adt-radius);
	border: var(--adt-border);
	box-shadow: var(--adt-shadow);
	overflow: hidden;
	background: var(--admin-table-shell-bg);
}

.admin-data-table__scroll {
	overflow-x: auto;
	-webkit-overflow-scrolling: touch;
}

.admin-data-table__table {
	width: 100%;
	margin-bottom: 0;
	border-collapse: separate;
	border-spacing: 0;
}

.admin-data-table__table thead tr:first-child th:first-child {
	border-top-left-radius: var(--adt-radius);
}

.admin-data-table__table thead tr:first-child th:last-child {
	border-top-right-radius: var(--adt-radius);
}

.admin-data-table__table thead th {
	background-color: var(--adt-header-bg);
	color: var(--adt-header-color);
	font-weight: 600;
	font-size: var(--adt-header-font-size);
	text-transform: none;
	letter-spacing: 0.02em;
	border: none !important;
	padding: 14px 16px;
	vertical-align: middle;
}

.admin-data-table__table thead th.is-sortable {
	cursor: pointer;
	user-select: none;
}

.admin-data-table__table thead th.is-sortable:hover {
	filter: brightness(1.06);
}

.admin-data-table__table thead th.admin-data-table__col-index,
.admin-data-table__table tbody td.admin-data-table__col-index {
	width: 3.25rem;
	max-width: 4rem;
	text-align: center;
	font-variant-numeric: tabular-nums;
	vertical-align: middle !important;
}

.admin-data-table__th-inner {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	gap: 8px;
	min-height: 20px;
}

.admin-data-table__th-title {
	flex: 1;
	min-width: 0;
}

.admin-data-table__sort {
	display: inline-flex;
	align-items: center;
	flex-shrink: 0;
	opacity: 0.95;
}

.admin-data-table__sort-hint {
	display: flex;
	flex-direction: column;
	align-items: center;
	line-height: 1;
	opacity: 0.45;
	margin-top: 1px;
}

.admin-data-table__sort-icon {
	display: block;
	flex-shrink: 0;
}

.admin-data-table__sort-icon--faint {
	margin-top: -3px;
}

.admin-data-table__sort-hint .admin-data-table__sort-icon--faint:first-child {
	margin-top: 0;
}

.admin-data-table__table tbody td {
	padding: 12px 16px;
	vertical-align: middle;
	border-top: 1px solid var(--admin-table-row-border-color);
	font-size: var(--adt-font-size);
}

.admin-data-table__table tbody tr:first-child:not(.admin-data-table__state-row) td {
	border-top: 1px solid var(--admin-table-border-color);
}

.admin-data-table__table tbody tr.admin-data-table__state-row td {
	border-top: none;
	padding: 0;
}

.admin-data-table__table tbody tr:not(.admin-data-table__state-row):hover td {
	background-color: var(--adt-row-hover);
}

.admin-data-table__state {
	text-align: center;
	padding: 40px 24px;
	color: var(--adt-text-muted);
}

.admin-data-table__state-icon {
	font-size: 40px;
	opacity: 0.45;
	margin-bottom: 12px;
	display: block;
	margin-left: auto;
	margin-right: auto;
}

.admin-data-table__state-title {
	font-size: 16px;
	font-weight: 600;
	color: var(--admin-table-text-strong);
	margin-bottom: 4px;
}

.admin-data-table__state-hint {
	font-size: 13px;
	color: var(--adt-text-muted);
}

.admin-data-table__footer {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 12px 16px;
	padding: 12px 16px;
	border-top: var(--adt-border);
	background: var(--adt-footer-bg);
}

.admin-data-table__footer-left {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 8px;
}

.admin-data-table__footer-label {
	margin: 0;
	font-size: 12px;
	font-weight: normal;
	color: var(--adt-text-muted);
}

.admin-data-table__page-size {
	width: auto;
	min-width: 64px;
	display: inline-block;
	height: 30px;
	padding: 4px 8px;
	font-size: 12px;
}

.admin-data-table__footer-mid {
	font-size: 13px;
	font-weight: 500;
	color: var(--admin-table-footer-mid-text);
}

.admin-data-table__footer-right {
	flex-shrink: 0;
}

.admin-data-table__pagination {
	margin: 0;
}

.admin-data-table__pagination>li>a {
	padding: 5px 12px;
	font-size: 12px;
	color: var(--adt-header-bg);
}

.admin-data-table__pagination>li.disabled>a {
	color: var(--admin-table-pagination-disabled);
	pointer-events: none;
	cursor: default;
}
</style>
