'use strict';

const express = require('express');
const database = require('./database');

function createRouter(getActiveInstance) {
	const router = express.Router();

	function dbAvailable() {
		return database && database.db;
	}

	// ========================================
	// Read-only routes (query DB singleton directly)
	// ========================================

	router.get('/api/inventory', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const items = await database.searchItems(req.query.q);
			res.json({ items });
		} catch (error) {
			console.error('API Error /api/inventory:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/inventory/:itemId', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const itemId = parseInt(req.params.itemId);
			const item = await database.getItemDetails(itemId);
			if (!item) return res.status(404).json({ error: 'Item not found' });
			res.json({ item });
		} catch (error) {
			console.error('API Error /api/inventory/:itemId:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/chests', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const chests = await database.getChests();
			res.json({ chests });
		} catch (error) {
			console.error('API Error /api/chests:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/chests/:id', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const chestId = parseInt(req.params.id);
			const chest = await database.getChestById(chestId);
			if (!chest) return res.status(404).json({ error: 'Chest not found' });
			const shulkers = await database.getShulkersByChest(chestId);
			res.json({ chest, shulkers });
		} catch (error) {
			console.error('API Error /api/chests/:id:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/stats', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const stats = await database.getStats();
			res.json(stats);
		} catch (error) {
			console.error('API Error /api/stats:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/trades', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const limit = parseInt(req.query.limit) || 50;
			const trades = await database.getRecentTrades(limit);
			res.json({ trades });
		} catch (error) {
			console.error('API Error /api/trades:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/pending/:playerName', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const pending = await database.getPendingWithdrawals(req.params.playerName);
			res.json({ pending });
		} catch (error) {
			console.error('API Error /api/pending/:playerName:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/map', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const chests = await database.getChestsWithSummary();
			res.json({ chests });
		} catch (error) {
			console.error('API Error /api/map:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/chests/:id/contents', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const chestId = parseInt(req.params.id);
			const contents = await database.getChestContents(chestId);
			if (!contents) return res.status(404).json({ error: 'Chest not found' });
			res.json(contents);
		} catch (error) {
			console.error('API Error /api/chests/:id/contents:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/shulkers/:id', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const shulkerId = parseInt(req.params.id);
			const shulker = await database.getShulkerWithItems(shulkerId);
			if (!shulker) return res.status(404).json({ error: 'Shulker not found' });
			res.json({ shulker });
		} catch (error) {
			console.error('API Error /api/shulkers/:id:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/players', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const players = await database.getAllPlayers();
			res.json({ players });
		} catch (error) {
			console.error('API Error /api/players:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.get('/api/special-items', async (req, res) => {
		if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });
		try {
			const items = await database.getSpecialItems();
			const parsed = items.map(item => {
				let nbt = null;
				try { nbt = JSON.parse(item.nbt_data); } catch (e) {}
				return { ...item, nbt_parsed: nbt };
			});
			res.json({ items: parsed });
		} catch (error) {
			console.error('API Error /api/special-items:', error);
			res.status(500).json({ error: error.message });
		}
	});

	// ========================================
	// Storage status (for command polling)
	// ========================================

	router.get('/api/storage/status', (req, res) => {
		try {
			const { plugin } = getActiveInstance(req.query.bot);
			if (!plugin) {
				return res.json({ busy: false, command: null });
			}
			res.json({
				busy: !!plugin._busy,
				command: plugin._currentCommand || null,
			});
		} catch (error) {
			res.json({ busy: false, command: null });
		}
	});

	// ========================================
	// Action routes (need live plugin instance)
	// ========================================

	router.post('/api/withdraw-special', async (req, res) => {
		try {
			const { playerName, shulkerItemId } = req.body;
			if (!playerName || !shulkerItemId) {
				return res.status(400).json({ error: 'Missing playerName or shulkerItemId' });
			}
			if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });

			const hasPermission = await database.checkPermission(playerName, 'team');
			if (!hasPermission) {
				return res.status(403).json({ error: `Player ${playerName} does not have permission` });
			}

			const { plugin, bot } = getActiveInstance(req.query.bot);
			if (!plugin && !bot) {
				return res.status(503).json({ error: 'Storage plugin not available' });
			}

			const parsedId = parseInt(shulkerItemId);
			const connecting = !plugin;

			if (plugin) {
				plugin.handleWithdrawSpecialItem(playerName, parsedId)
					.catch(err => console.error('Web special withdraw error:', err));
			} else {
				bot.ensureConnected(async () => {
					const p = bot.plunginsLoaded['Storage'];
					if (!p) throw new Error('Storage plugin not loaded after connect');
					await p.handleWithdrawSpecialItem(playerName, parsedId);
				}).catch(err => console.error('On-demand special withdraw error:', err));
			}

			res.json({
				status: 'queued',
				connecting,
				message: connecting
					? `Bot connecting, special item withdrawal queued for ${playerName}...`
					: `Special item withdrawal queued for ${playerName}`
			});
		} catch (error) {
			console.error('API Error /api/withdraw-special:', error);
			res.status(500).json({ error: error.message });
		}
	});

	router.post('/api/withdraw', async (req, res) => {
		try {
			const { playerName, itemName, count, mode } = req.body;
			if (!playerName || !itemName || !count) {
				return res.status(400).json({ error: 'Missing playerName, itemName, or count' });
			}
			if (!dbAvailable()) return res.status(503).json({ error: 'Storage database not initialized' });

			const hasPermission = await database.checkPermission(playerName, 'team');
			if (!hasPermission) {
				return res.status(403).json({ error: `Player ${playerName} does not have permission` });
			}

			const { plugin, bot } = getActiveInstance(req.query.bot);
			if (!plugin && !bot) {
				return res.status(503).json({ error: 'Storage plugin not available' });
			}

			const parsedCount = parseInt(count);
			const connecting = !plugin;

			const runTask = async (p) => {
				if (mode === 'shulkers') {
					await p.handleWithdrawShulkers(playerName, itemName, parsedCount);
				} else {
					await p.handleWithdrawRequest(playerName, itemName, parsedCount);
				}
			};

			if (plugin) {
				runTask(plugin).catch(err => console.error('Web withdraw error:', err));
			} else {
				bot.ensureConnected(async () => {
					const p = bot.plunginsLoaded['Storage'];
					if (!p) throw new Error('Storage plugin not loaded after connect');
					await runTask(p);
				}).catch(err => console.error('On-demand withdraw error:', err));
			}

			const modeLabel = mode === 'shulkers'
				? `${parsedCount} shulker(s) of ${itemName}`
				: `${parsedCount}x ${itemName}`;
			res.json({
				status: 'queued',
				connecting,
				message: connecting
					? `Bot connecting, withdrawal of ${modeLabel} queued for ${playerName}...`
					: `Withdrawal of ${modeLabel} queued for ${playerName}`
			});
		} catch (error) {
			console.error('API Error /api/withdraw:', error);
			res.status(500).json({ error: error.message });
		}
	});

	return router;
}

const webUI = {
	tabId: 'storage',
	tabLabel: 'Storage',
	tabOrder: 10,
	sidebarHtml: `
		<div class="search-wrap">
			<div class="ac-wrap">
				<input type="text" id="search" placeholder="Search items..." autocomplete="off">
				<div class="ac-list" id="ac-search"></div>
			</div>
		</div>
		<div class="inv-list" id="invList">
			<div style="padding:20px;color:#6b7280;text-align:center">Loading...</div>
		</div>
	`,
	sidebarJs: `
		// Sidebar search autocomplete
		setupAC('search','ac-search',
			q=>{
				const lower=q.toLowerCase();
				return allItems
					.filter(i=>!lower||i.item_name.includes(lower))
					.slice(0,15)
					.map(i=>({label:i.item_name,value:i.item_name,extra:fmt(i.total_count)}));
			},
			val=>{if(val!==null){filterItems()}else{filterItems()}}
		);
	`,
	onTabActive: 'onStorageTabActive',
	html: `
		<div style="margin-bottom:8px"><span class="last-updated" id="ts-storage"></span></div>
		<div class="stats-row" id="stats"></div>
		<div class="tabs storage-sub-tabs">
			<div class="tab active" onclick="switchStorageSubTab('inventory')">Inventory</div>
			<div class="tab" onclick="switchStorageSubTab('map')">Storage Map</div>
			<div class="tab" onclick="switchStorageSubTab('special')">Special Items</div>
			<div class="tab" onclick="switchStorageSubTab('withdraw')">Withdraw</div>
		</div>
		<div id="stab-inventory" class="stab-content active" style="margin-top:16px">
			<table>
				<thead>
					<tr>
						<th onclick="sortItems('item_name')">Item <span class="arrow" id="sort-item_name"></span></th>
						<th onclick="sortItems('total_count')">Count <span class="arrow" id="sort-total_count"></span></th>
					</tr>
				</thead>
				<tbody id="invTable"></tbody>
			</table>
		</div>
		<div id="stab-map" class="stab-content" style="margin-top:16px">
			<div class="map-legend">
				<span><span class="dot" style="background:#6b7280"></span> Empty</span>
				<span><span class="dot" style="background:#f59e0b"></span> Partial</span>
				<span><span class="dot" style="background:#10b981"></span> Full</span>
				<span><span class="dot" style="background:#ef4444"></span> Loose Items</span>
			</div>
			<div id="mapArea"></div>
		</div>
		<div id="stab-special" class="stab-content" style="margin-top:16px">
			<div class="panel">
				<h3>Named & Custom Items</h3>
				<p style="font-size:.85em;color:#9ca3af;margin-bottom:12px">Items with custom names, lore, or special properties stored separately from regular items.</p>
				<input type="text" id="specialSearch" placeholder="Search special items..." oninput="filterSpecialItems()" style="width:100%;padding:10px 12px;border:1px solid #374151;border-radius:6px;background:#111827;color:#e5e7eb;font-size:.9em;margin-bottom:12px;box-sizing:border-box">
				<div id="specialItems"><div style="padding:20px;color:#6b7280;text-align:center">Click tab to load...</div></div>
			</div>
		</div>
		<div id="stab-withdraw" class="stab-content" style="margin-top:16px">
			<div class="panel">
				<h3>Request Withdrawal</h3>
				<form class="withdraw-form" id="withdrawForm">
					<div class="ac-wrap" style="flex:1;min-width:120px">
						<input type="text" id="wPlayer" placeholder="Player name" autocomplete="off" style="width:100%">
						<div class="ac-list" id="ac-wPlayer"></div>
					</div>
					<div class="ac-wrap" style="flex:1;min-width:150px">
						<input type="text" id="wItem" placeholder="Item name (e.g. diamond)" autocomplete="off" style="width:100%">
						<div class="ac-list" id="ac-wItem"></div>
					</div>
					<select id="wMode" style="padding:8px;border:1px solid #374151;border-radius:6px;background:#111827;color:#e5e7eb;font-size:.85em" onchange="updateWithdrawMode()">
						<option value="items">Items</option>
						<option value="shulkers">Shulkers</option>
					</select>
					<input type="number" id="wCount" placeholder="Count" min="1" value="1" style="width:80px">
					<button type="submit">Request</button>
				</form>
				<div id="withdrawStatus"></div>
			</div>
		</div>
		<div class="detail-panel" id="detailPanel">
			<button class="close" onclick="closeDetail()">&times;</button>
			<div id="detailContent"></div>
		</div>
		<div class="tooltip" id="tooltip" style="display:none"></div>
	`,
	css: `
		.stats-row{display:grid;grid-template-columns:repeat(7,1fr);gap:12px;margin-bottom:20px}
		.stat{background:#1f2937;border:1px solid #374151;border-radius:8px;padding:14px;text-align:center}
		.stat .val{font-size:1.6em;font-weight:700;color:#60a5fa}
		.stat .lbl{font-size:.75em;color:#9ca3af;margin-top:2px}
		.search-wrap{padding:12px;border-bottom:1px solid #374151}
		.search-wrap input{width:100%;padding:10px 12px;border:1px solid #374151;border-radius:6px;background:#111827;color:#e5e7eb;font-size:.9em}
		.search-wrap input:focus{outline:none;border-color:#2563eb}
		.inv-list{flex:1;overflow:auto;padding:4px 0}
		.inv-item{display:flex;justify-content:space-between;align-items:center;padding:8px 14px;cursor:pointer;border-bottom:1px solid #1f2937}
		.inv-item:hover{background:#374151}
		.inv-item .name{font-size:.85em}
		.inv-item .count{background:#2563eb;color:#fff;padding:2px 8px;border-radius:10px;font-size:.8em;font-weight:600;min-width:40px;text-align:center}
		.inv-item .count.large{background:#059669}
		table{width:100%;border-collapse:collapse}
		th{text-align:left;padding:10px 12px;background:#1f2937;border-bottom:1px solid #374151;color:#9ca3af;font-size:.8em;cursor:pointer;user-select:none;position:sticky;top:0}
		th:hover{color:#e5e7eb}
		th .arrow{margin-left:4px;font-size:.7em}
		td{padding:8px 12px;border-bottom:1px solid #1f2937;font-size:.85em}
		tr:hover td{background:#1f2937}
		.map-container{position:relative;background:#0f172a;border:1px solid #374151;border-radius:8px;overflow:auto}
		.map-level{margin-bottom:16px}
		.map-level h3{color:#60a5fa;font-size:.9em;margin-bottom:8px;padding:8px 12px;background:#1f2937;border-radius:6px 6px 0 0}
		.map-grid{position:relative;margin:0 auto}
		.map-chest{position:absolute;border-radius:3px;cursor:pointer;font-size:7px;display:flex;align-items:center;justify-content:center;color:#000;font-weight:700;transition:transform .1s;border:1px solid rgba(0,0,0,.3)}
		.map-chest:hover{transform:scale(1.5);z-index:10}
		.map-chest.empty{background:#6b7280}
		.map-chest.partial{background:#f59e0b}
		.map-chest.full{background:#10b981}
		.map-chest.loose{background:#ef4444}
		.map-chest.unscanned{background:#8b5cf6}
		.map-legend{display:flex;gap:16px;padding:12px;font-size:.8em;color:#9ca3af}
		.map-legend span{display:flex;align-items:center;gap:4px}
		.map-legend .dot{width:10px;height:10px;border-radius:2px}
		.panel{background:#1f2937;border:1px solid #374151;border-radius:8px;padding:16px;margin-bottom:16px}
		.panel h3{color:#60a5fa;font-size:1em;margin-bottom:12px}
		.shulker-grid{display:grid;grid-template-columns:repeat(9,1fr);gap:2px;background:#374151;padding:2px;border-radius:4px;margin-bottom:8px}
		.shulker-slot{background:#111827;aspect-ratio:1;display:flex;align-items:center;justify-content:center;font-size:.65em;color:#9ca3af;border-radius:2px;position:relative}
		.shulker-slot.filled{background:#1e3a5f;color:#60a5fa}
		.shulker-slot .slot-count{position:absolute;bottom:1px;right:2px;font-size:.6em;color:#f59e0b}
		.withdraw-form{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:8px}
		.withdraw-form input{padding:8px;border:1px solid #374151;border-radius:6px;background:#111827;color:#e5e7eb;font-size:.85em}
		.withdraw-form button{background:#2563eb;color:#fff;border:none;padding:8px 14px;border-radius:6px;cursor:pointer;font-size:.85em}
		#withdrawStatus{font-size:.85em;min-height:20px}
		.detail-panel{position:fixed;right:0;top:57px;width:400px;height:calc(100vh - 57px);background:#1f2937;border-left:1px solid #374151;overflow:auto;padding:16px;z-index:50;transform:translateX(100%);transition:transform .2s}
		.detail-panel.open{transform:translateX(0)}
		.detail-panel .close{position:absolute;top:12px;right:12px;background:none;border:none;color:#9ca3af;font-size:1.2em;cursor:pointer}
		.detail-panel .close:hover{color:#e5e7eb}
		.chest-info{margin-bottom:16px}
		.chest-info p{font-size:.85em;color:#9ca3af;margin:4px 0}
		.shulker-card{background:#111827;border:1px solid #374151;border-radius:6px;padding:10px;margin-bottom:8px}
		.shulker-card h4{font-size:.85em;color:#f59e0b;margin-bottom:6px}
		.shulker-card .items{font-size:.8em;color:#9ca3af}
		.shulker-card .items span{display:inline-block;background:#1f2937;padding:2px 6px;border-radius:4px;margin:2px}
		.ac-wrap{position:relative}
		.ac-list{position:absolute;top:100%;left:0;right:0;background:#1f2937;border:1px solid #374151;border-top:none;border-radius:0 0 6px 6px;max-height:220px;overflow:auto;z-index:60;display:none}
		.ac-list.open{display:block}
		.ac-opt{padding:8px 12px;cursor:pointer;font-size:.85em;display:flex;justify-content:space-between;align-items:center}
		.ac-opt:hover,.ac-opt.active{background:#374151}
		.ac-opt .ac-count{color:#9ca3af;font-size:.75em}
		.ac-opt .ac-match{color:#60a5fa;font-weight:600}
		.special-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px}
		.special-card{background:#111827;border:1px solid #374151;border-radius:8px;padding:14px;transition:border-color .2s}
		.special-card:hover{border-color:#60a5fa}
		.special-card .sp-name{font-size:1em;font-weight:700;color:#f59e0b;margin-bottom:4px}
		.special-card .sp-base{font-size:.8em;color:#9ca3af;margin-bottom:8px}
		.special-card .sp-enchants{font-size:.8em;color:#a78bfa;margin-bottom:4px}
		.special-card .sp-lore{font-size:.8em;color:#6ee7b7;font-style:italic;margin-bottom:4px}
		.special-card .sp-count{font-size:.85em;color:#60a5fa;margin-bottom:8px}
		.special-card .sp-withdraw{display:flex;gap:6px;align-items:center;margin-top:8px}
		.special-card .sp-withdraw input{padding:6px;border:1px solid #374151;border-radius:4px;background:#1f2937;color:#e5e7eb;font-size:.8em;width:120px}
		.special-card .sp-withdraw button{background:#2563eb;color:#fff;border:none;padding:6px 12px;border-radius:4px;cursor:pointer;font-size:.8em}
		.special-card .sp-withdraw button:hover{background:#1d4ed8}
		.special-card .sp-status{font-size:.8em;margin-top:4px;min-height:16px}
		.stab-content{display:none}
		.stab-content.active{display:block}
		.storage-sub-tabs{display:flex;border-bottom:1px solid #374151}
		.db-unavailable{padding:20px;color:#f59e0b;text-align:center;font-size:.9em}
	`,
	js: `
let allItems=[], mapData=[], sortKey='total_count', sortDir=-1;
let specialLoaded=false, storageSubTab='inventory';

function onStorageTabActive() {
	if (allItems.length === 0) { loadStats(); loadInventory(); loadPlayers(); }
}

function switchStorageSubTab(name) {
	storageSubTab = name;
	document.querySelectorAll('.storage-sub-tabs .tab').forEach(t => t.classList.remove('active'));
	document.querySelectorAll('.stab-content').forEach(t => t.classList.remove('active'));
	const el = document.getElementById('stab-'+name);
	if (el) el.classList.add('active');
	const subNames=['inventory','map','special','withdraw'];
	const tabs = document.querySelectorAll('.storage-sub-tabs .tab');
	const idx = subNames.indexOf(name);
	if (idx >= 0 && tabs[idx]) tabs[idx].classList.add('active');
	if (name === 'map' && mapData.length === 0) loadMap();
	if (name === 'special' && !specialLoaded) loadSpecialItems();
}

async function loadStats() {
	try {
		const r = await fetch('/api/stats');
		if (!r.ok) { document.getElementById('stats').innerHTML=''; return; }
		const s = await r.json();
		document.getElementById('stats').innerHTML =
			stat(fmt(s.totalItems||0),'Total Items')+
			stat(fmt(s.totalShulkers||0),'Shulkers')+
			stat(fmt(s.totalChests||0),'Chests')+
			stat(fmt(s.emptyShulkers||0),'Empty')+
			stat(fmt(s.recentTrades||0),'Trades (24h)')+
			stat(fmt(s.looseItemCount||0),'Loose Items')+
			stat((s.totalChestSlots ? Math.round((s.totalShulkers||0)/(s.totalChestSlots)*100) : 0)+'%','Storage Full');
	} catch(e) {
		document.getElementById('stats').innerHTML='';
	}
}
function stat(v,l){return '<div class="stat"><div class="val">'+v+'</div><div class="lbl">'+l+'</div></div>'}

async function loadInventory() {
	try {
		const r = await fetch('/api/inventory');
		if (!r.ok) {
			document.getElementById('invList').innerHTML='<div class="db-unavailable">Storage database not available</div>';
			document.getElementById('invTable').innerHTML='';
			return;
		}
		const d = await r.json();
		allItems = d.items || [];
		renderSidebar(allItems);
		renderTable(allItems);
		updateTimestamp('ts-storage');
	} catch(e) {
		document.getElementById('invList').innerHTML='<div style="padding:20px;color:#ef4444">Error loading</div>';
	}
}

function renderSidebar(items) {
	if (!items.length) {
		document.getElementById('invList').innerHTML='<div style="padding:20px;color:#6b7280">No items found</div>';
		return;
	}
	document.getElementById('invList').innerHTML = items.map(i =>
		'<div class="inv-item" onclick="highlightItem(\\''+i.item_name+'\\')">' +
		'<span class="name">'+fmtName(i.item_name)+'</span>' +
		'<span class="count'+(i.total_count>=1000?' large':'')+'">'+fmt(i.total_count)+'</span></div>'
	).join('');
}

function renderTable(items) {
	const sorted = [...items].sort((a,b) => {
		const av=a[sortKey], bv=b[sortKey];
		if (typeof av==='string') return sortDir*av.localeCompare(bv);
		return sortDir*(av-bv);
	});
	document.querySelectorAll('th .arrow').forEach(a=>a.textContent='');
	const el=document.getElementById('sort-'+sortKey);
	if(el)el.textContent=sortDir>0?'\\u25B2':'\\u25BC';

	document.getElementById('invTable').innerHTML = sorted.map(i =>
		'<tr><td>'+fmtName(i.item_name)+'</td><td>'+fmt(i.total_count)+'</td></tr>'
	).join('');
}

function sortItems(key) {
	if (sortKey===key) sortDir*=-1;
	else { sortKey=key; sortDir=key==='total_count'?-1:1; }
	const q=document.getElementById('search').value.toLowerCase();
	const filtered=q?allItems.filter(i=>i.item_name.includes(q)):allItems;
	renderTable(filtered);
}

function filterItems() {
	const q=document.getElementById('search').value.toLowerCase();
	const filtered=q?allItems.filter(i=>i.item_name.includes(q)):allItems;
	renderSidebar(filtered);
	renderTable(filtered);
}

function highlightItem(name) {
	document.getElementById('search').value=name;
	filterItems();
}

// === MAP ===
async function loadMap() {
	try {
		const r = await fetch('/api/map');
		if (!r.ok) { document.getElementById('mapArea').innerHTML='<div class="db-unavailable">Storage database not available</div>'; return; }
		const d = await r.json();
		mapData = d.chests || [];
		renderMap(mapData);
	} catch(e) {
		document.getElementById('mapArea').innerHTML='<div style="padding:20px;color:#ef4444">Failed to load map</div>';
	}
}

function renderMap(chests) {
	if (!chests.length) {
		document.getElementById('mapArea').innerHTML='<div style="padding:20px;color:#6b7280">No chests found</div>';
		return;
	}

	const levels = {};
	let minX=Infinity,maxX=-Infinity,minZ=Infinity,maxZ=-Infinity;
	for (const c of chests) {
		if(!levels[c.pos_y]) levels[c.pos_y]=[];
		levels[c.pos_y].push(c);
		minX=Math.min(minX,c.pos_x); maxX=Math.max(maxX,c.pos_x);
		minZ=Math.min(minZ,c.pos_z); maxZ=Math.max(maxZ,c.pos_z);
	}

	const scale=18, pad=20;
	const w=(maxX-minX+2)*scale+pad*2;
	const h=(maxZ-minZ+2)*scale+pad*2;

	const sortedYs = Object.keys(levels).sort((a,b)=>Number(b)-Number(a));
	let html='';

	for (const y of sortedYs) {
		html+='<div class="map-level"><h3>Level Y='+y+' ('+levels[y].length+' chests)</h3>';
		html+='<div class="map-grid" style="width:'+w+'px;height:'+h+'px;position:relative">';

		for (let x=minX; x<=maxX; x++) {
			const px=(x-minX)*scale+pad;
			html+='<div style="position:absolute;left:'+px+'px;top:0;width:1px;height:100%;background:#1e293b"></div>';
		}
		for (let z=minZ; z<=maxZ; z++) {
			const py=(z-minZ)*scale+pad;
			html+='<div style="position:absolute;top:'+py+'px;left:0;height:1px;width:100%;background:#1e293b"></div>';
		}

		for (const c of levels[y]) {
			const px=(c.pos_x-minX)*scale+pad;
			const py=(c.pos_z-minZ)*scale+pad;
			const cw=c.chest_type==='double'?scale*2-2:scale-2;
			const cls=c.loose_item_count>0?'loose':c.shulker_count===0?'empty':c.total_items===0?'empty':c.shulker_count>20?'full':'partial';
			const focuses=(c.item_focuses||'').split(',').filter(Boolean).slice(0,3).map(fmtName).join(', ')||'Empty';
			const looseLabel=c.loose_item_count>0?'\\\\n'+c.loose_item_count+' loose item(s)':'';

			html+='<div class="map-chest '+cls+'" style="left:'+px+'px;top:'+py+'px;width:'+cw+'px;height:'+(scale-2)+'px" '+
				'onclick="showChestDetail('+c.id+')" '+
				'onmouseenter="showTooltip(event,\\''+c.chest_type+' chest ('+c.pos_x+','+c.pos_y+','+c.pos_z+')\\\\n'+
				c.shulker_count+' shulkers, '+fmt(c.total_items)+' items\\\\n'+focuses.replace(/'/g,"\\\\'")+looseLabel+'\\')" '+
				'onmouseleave="hideTooltip()">'+
				c.shulker_count+'</div>';
		}
		html+='</div></div>';
	}

	document.getElementById('mapArea').innerHTML=html;
}

function showTooltip(e, text) {
	const t=document.getElementById('tooltip');
	t.innerHTML=text.replace(/\\\\n/g,'<br>');
	t.style.display='block';
	t.style.left=(e.clientX+12)+'px';
	t.style.top=(e.clientY+12)+'px';
}
function hideTooltip(){document.getElementById('tooltip').style.display='none'}

// === CHEST DETAIL ===
async function showChestDetail(chestId) {
	const panel=document.getElementById('detailPanel');
	const content=document.getElementById('detailContent');
	content.innerHTML='<p style="color:#6b7280">Loading...</p>';
	panel.classList.add('open');

	try {
		const r=await fetch('/api/chests/'+chestId+'/contents');
		const d=await r.json();
		const c=d.chest;
		let html='<div class="chest-info"><h3>Chest #'+c.id+'</h3>';
		html+='<p>Position: ('+c.pos_x+', '+c.pos_y+', '+c.pos_z+')</p>';
		html+='<p>Type: '+c.chest_type+'</p>';
		html+='<p>Category: '+(c.category||'none')+'</p></div>';

		const shulkers=d.shulkers||[];
		html+='<h3 style="color:#60a5fa;margin-bottom:8px">'+shulkers.length+' Shulkers</h3>';

		for (const s of shulkers) {
			html+='<div class="shulker-card">';
			html+='<h4>Slot '+s.slot+' - '+(s.item_focus?fmtName(s.item_focus):'Empty')+' ('+s.slot_count+'/27 slots, '+fmt(s.total_items)+' items)</h4>';

			if (s.item_summary) {
				const items=s.item_summary.split(',').reduce((acc,pair)=>{
					const[name,cnt]=pair.split(':');
					if(!acc[name])acc[name]=0;
					acc[name]+=parseInt(cnt)||0;
					return acc;
				},{});
				html+='<div class="items">';
				for(const[name,cnt] of Object.entries(items).sort((a,b)=>b[1]-a[1])) {
					html+='<span>'+fmtName(name)+': '+cnt+'</span>';
				}
				html+='</div>';
			} else {
				html+='<div class="items"><span style="color:#6b7280">Empty</span></div>';
			}
			html+='</div>';
		}
		content.innerHTML=html;
	} catch(e) {
		content.innerHTML='<p style="color:#ef4444">Failed to load chest</p>';
	}
}

function closeDetail(){document.getElementById('detailPanel').classList.remove('open')}

// === WITHDRAW ===
function updateWithdrawMode(){
	const mode=document.getElementById('wMode').value;
	const countInput=document.getElementById('wCount');
	if(mode==='shulkers'){countInput.placeholder='Shulkers';countInput.max=12;if(parseInt(countInput.value)>12)countInput.value=12}
	else{countInput.placeholder='Count';countInput.removeAttribute('max')}
}
document.getElementById('withdrawForm').addEventListener('submit', async(e)=>{
	e.preventDefault();
	const p=document.getElementById('wPlayer').value.trim();
	const i=document.getElementById('wItem').value.trim();
	const c=parseInt(document.getElementById('wCount').value);
	const mode=document.getElementById('wMode').value;
	const st=document.getElementById('withdrawStatus');
	if(!p||!i||!c){st.textContent='Fill all fields';st.style.color='#ef4444';return}
	try{
		const r=await fetch('/api/withdraw',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({playerName:p,itemName:i,count:c,mode:mode})});
		const d=await r.json();
		st.textContent=r.ok?(d.message||'Queued'):(d.error||'Failed');
		st.style.color=r.ok?'#60a5fa':'#ef4444';
	}catch(e){st.textContent='Network error';st.style.color='#ef4444'}
});

// Withdraw player autocomplete
setupAC('wPlayer','ac-wPlayer',
	q=>{
		const lower=q.toLowerCase();
		return playerNames.filter(p=>!lower||p.toLowerCase().includes(lower)).map(p=>({label:p,value:p}));
	}
);

// Withdraw item autocomplete
setupAC('wItem','ac-wItem',
	q=>{
		const lower=q.toLowerCase();
		return allItems
			.filter(i=>!lower||i.item_name.includes(lower))
			.slice(0,15)
			.map(i=>({label:i.item_name,value:i.item_name,extra:fmt(i.total_count)}));
	},
	val=>{
		if(val){
			const item=allItems.find(i=>i.item_name===val);
			if(item){document.getElementById('wCount').max=item.total_count}
		}
	}
);

// Parse Minecraft text: JSON chat components or section-sign formatted strings
function parseMcText(raw) {
	if (!raw) return '';
	if (typeof raw !== 'string') return String(raw);
	// Strip surrounding quotes if present
	let s = raw;
	if (s.startsWith('"') && s.endsWith('"')) s = s.slice(1, -1);
	// Try parsing as JSON chat component
	try {
		const obj = JSON.parse(s);
		if (typeof obj === 'object' && obj !== null) return extractChatText(obj);
	} catch(e) {}
	// Try parsing the original (unstripped) as JSON too
	try {
		const obj = JSON.parse(raw);
		if (typeof obj === 'object' && obj !== null) return extractChatText(obj);
	} catch(e) {}
	// Fall back to stripping section-sign codes
	return stripMcCodes(s);
}

function extractChatText(obj) {
	if (typeof obj === 'string') return stripMcCodes(obj);
	let text = '';
	if (obj.text) text += obj.text;
	if (Array.isArray(obj.extra)) text += obj.extra.map(extractChatText).join('');
	if (Array.isArray(obj)) text += obj.map(extractChatText).join('');
	return stripMcCodes(text);
}

function stripMcCodes(s) {
	return s.replace(/\\u00a7[0-9a-fk-or]/gi, '').replace(/§[0-9a-fk-or]/gi, '');
}

let allSpecialItems = [];

function renderSpecialCard(item) {
	const nbt = item.nbt_parsed || {};
	const displayName = parseMcText(nbt.displayName || '');
	const enchants = (nbt.enchantments || []).map(e => fmtName(String(e.id).replace('minecraft:','')) + ' ' + toRoman(e.level)).join(', ');
	const loreLines = (nbt.lore || []).map(l => parseMcText(l)).filter(Boolean);

	return '<div class="special-card">' +
		(displayName ? '<div class="sp-name">' + escHtml(displayName) + '</div>' : '') +
		'<div class="sp-base">' + fmtName(item.item_name) + '</div>' +
		(enchants ? '<div class="sp-enchants">' + escHtml(enchants) + '</div>' : '') +
		(loreLines.length ? '<div class="sp-lore">' + loreLines.map(l => escHtml(l)).join('<br>') + '</div>' : '') +
		'<div class="sp-count">x' + item.count + '</div>' +
		'<div class="sp-withdraw">' +
			'<input type="text" placeholder="Player name" id="sp-player-' + item.id + '">' +
			'<button onclick="withdrawSpecial(' + item.id + ')">Withdraw</button>' +
		'</div>' +
		'<div class="sp-status" id="sp-status-' + item.id + '"></div>' +
	'</div>';
}

function renderSpecialItems(items) {
	const container = document.getElementById('specialItems');
	if (items.length === 0) {
		container.innerHTML='<div style="padding:20px;color:#6b7280;text-align:center">No special items found</div>';
		return;
	}
	container.innerHTML = '<div class="special-grid">' + items.map(renderSpecialCard).join('') + '</div>';
}

function filterSpecialItems() {
	const q = (document.getElementById('specialSearch').value || '').toLowerCase();
	if (!q) { renderSpecialItems(allSpecialItems); return; }
	const filtered = allSpecialItems.filter(item => {
		const nbt = item.nbt_parsed || {};
		const name = parseMcText(nbt.displayName || '').toLowerCase();
		const base = (item.item_name || '').toLowerCase();
		const lore = (nbt.lore || []).map(l => parseMcText(l).toLowerCase()).join(' ');
		const enchants = (nbt.enchantments || []).map(e => String(e.id).replace('minecraft:','')).join(' ').toLowerCase();
		return name.includes(q) || base.includes(q) || lore.includes(q) || enchants.includes(q);
	});
	renderSpecialItems(filtered);
}

async function loadSpecialItems() {
	const container = document.getElementById('specialItems');
	container.innerHTML='<div style="padding:20px;color:#6b7280;text-align:center">Loading...</div>';
	try {
		const r = await fetch('/api/special-items');
		if (!r.ok) { container.innerHTML='<div class="db-unavailable">Storage database not available</div>'; return; }
		const d = await r.json();
		allSpecialItems = d.items || [];
		specialLoaded = true;
		renderSpecialItems(allSpecialItems);
	} catch(e) {
		container.innerHTML='<div style="padding:20px;color:#ef4444">Failed to load special items</div>';
	}
}

async function withdrawSpecial(itemId) {
	const playerInput = document.getElementById('sp-player-' + itemId);
	const statusEl = document.getElementById('sp-status-' + itemId);
	const playerName = playerInput.value.trim();
	if (!playerName) {
		statusEl.textContent = 'Enter player name';
		statusEl.style.color = '#ef4444';
		return;
	}
	try {
		const r = await fetch('/api/withdraw-special', {
			method: 'POST',
			headers: {'Content-Type': 'application/json'},
			body: JSON.stringify({ playerName, shulkerItemId: itemId })
		});
		const d = await r.json();
		statusEl.textContent = r.ok ? (d.message || 'Queued') : (d.error || 'Failed');
		statusEl.style.color = r.ok ? '#60a5fa' : '#ef4444';
		if (r.ok) { specialLoaded = false; setTimeout(loadSpecialItems, 3000); }
	} catch(e) {
		statusEl.textContent = 'Network error';
		statusEl.style.color = '#ef4444';
	}
}

function toRoman(n) {
	if (!n || n <= 0) return '';
	const vals = [10,9,5,4,1];
	const syms = ['X','IX','V','IV','I'];
	let result = '';
	for (let i = 0; i < vals.length; i++) {
		while (n >= vals[i]) { result += syms[i]; n -= vals[i]; }
	}
	return result;
}

let playerNames=[];
async function loadPlayers(){
	try{const r=await fetch('/api/players');if(!r.ok)return;const d=await r.json();playerNames=(d.players||[]).map(p=>p.player_name)}catch(e){}
}

function storageLoadAll(){loadStats();loadInventory();loadPlayers();if(storageSubTab==='map')loadMap();if(storageSubTab==='special')loadSpecialItems()}
	`,
};

module.exports = { createRouter, webUI };
