_wrapperVersion = '1.0.0';
_minApiVersion = '1.0.0';
_maxApiVersion = '1.0.0';

_defaultTTL = 60000;

_apiConfig = {
	basePath: '/api/v1/'
};

if (!window.localStorage) {
	console.warn('Local Storage is not available, some features may not work');
}

// Generic driver functions
let _api = {
	get: async function (path) {
		const options = {
			headers: new Headers({ 'content-type': 'application/json' })
		};
		const response = await fetch(_apiConfig.basePath + path, options);
		// Handle the response
		if (!response.ok) {
			console.error('Failed to fetch:', response.statusText);
			_testPageFail(response.statusText);
			return;
		}
		const result = await response.json();
		// Handle the result, was json valid?
		if (!result) {
			// Is it a number instead?
			if (typeof result === 'number') {
				return result;
			}
			if (typeof result === 'boolean') {
				return result;
			}
			console.error('Invalid JSON response');
			_testPageFail('Invalid JSON response');
			return;
		}

		return result;
	},
	post: async function (path, data) {
		const options = {
			method: 'POST',
			headers: new Headers({ 'content-type': 'application/json' }),
			body: JSON.stringify(data)
		};
		const response = await fetch(_apiConfig.basePath + path, options);
		// Handle the response
		if (!response.ok) {
			_testPageFail(response.statusText);
			return;
		}
		const result = await response.json();
		// Handle the result, was json valid?
		if (!result) {
			_testPageFail('Invalid JSON response');
			return;
		}

		return result;
	},
	delete: async function (path, data) {
		const options = {
			method: 'DELETE',
			headers: new Headers({ 'content-type': 'application/json' }),
			body: JSON.stringify(data)
		};
		const response = await fetch(_apiConfig.basePath + path, options);
		// Handle the response
		if (!response.ok) {
			_testPageFail(response.statusText);
			return;
		}
		const result = await response.json();
		// Handle the result, was json valid?
		if (!result) {
			_testPageFail('Invalid JSON response');
			return;
		}

		return result;
	},
	patch: async function (path, data) {
		const options = {
			method: 'PATCH',
			headers: new Headers({ 'content-type': 'application/json' }),
			body: JSON.stringify(data)
		};
		const response = await fetch(_apiConfig.basePath + path, options);
		// Handle the response
		if (!response.ok) {
			_testPageFail(response.statusText);
			return;
		}
		const result = await response.json();
		// Handle the result, was json valid?
		if (!result) {
			_testPageFail('Invalid JSON response');
			return;
		}

		return result;
	}

};

function updateRow(tableName, id, data) {
	invalidateCache(tableName);
	return _api.patch(`${tableName}`, { id: id, ...data });
}

function deleteRow(tableName, id) {
	invalidateCache(tableName);
	return _api.delete(`${tableName}`, { id: id });
}

function getApiDescriptionByTable(tableName) {
	const keyDesc = `desc:${tableName}`;
	const keyTime = `${keyDesc}:time`;
	const keyTTL = `${keyDesc}:ttl`;

	// Retrieve cached data
	const description = JSON.parse(localStorage.getItem(keyDesc));
	const timeCreated = parseInt(localStorage.getItem(keyTime));
	const ttl = parseInt(localStorage.getItem(keyTTL));

	// Check if valid cached data exists
	if (description && timeCreated && ttl) {
		const currentTime = Date.now();
		const age = currentTime - parseInt(timeCreated, 10);
		if (age < parseInt(ttl, 10)) {
			// Return cached data immediately
			return Promise.resolve(description);
		} else {
			console.warn('Cached description expired; fetching new data');
			// Fetch new data, update cache, and return it
			return fetchAndUpdateCache(tableName);
		}
	} else {
		console.warn('No cached description; fetching from server');
		// Fetch data, update cache, and return it
		return fetchAndUpdateCache(tableName);
	}

	function fetchAndUpdateCache(tableName) {
		return _api
			.get(`${tableName}/describe`)
			.then((data) => {
				if (data) {
					// Update local storage with new data
					localStorage.setItem(keyDesc, JSON.stringify(data));
					localStorage.setItem(keyTime, Date.now().toString());
					localStorage.setItem(keyTTL, '60000'); // 60 seconds TTL
				}
				return data; // Return the fetched data
			})
			.catch((error) => {
				console.error('Failed to fetch description:', error);
				// Fallback to cached data if available (even if expired)
				return description || null;
			});
	}
}

function returnTableDataByTableName(tableName, search="", orderBy="asc", sort="", take=-1, skip=0) {
	var orderBy = orderBy.toLowerCase();
	if(orderBy == "") {
		orderBy = "asc";
	}
	var baseString = tableName + "?order=" + orderBy;
	if(sort && sort.length > 0) {
		baseString += "&sort=" + sort;
	}
	if(take > 0) {
		baseString += "&take=" + take;
	}
	if(skip > 0) {
		baseString += "&skip=" + skip;
	}

	if (search && search.length > 0) {
		return _api.get(baseString + '&search=' + search);
	} else {
		return _api.get(baseString);
	}
}

async function getCountByTable(tableName, search="") {
	let baseString = tableName + '?count=true';
	if (search && search.length > 0) {
		baseString += '&search=' + search;
	}
	// Stored in `data:count:${tableName}`
	let result = await _api.get(baseString);
	console.debug('Count result:', result);
	if (typeof result !== 'number') {
		_testPageWarn('Count was not a number, was: ' + result);
		console.warn('Count was not a number, was: ' + result);
		return -1;
	}
	return result;
}


function _testPageFail(reason) {
	document.getElementById('heroStatus').classList.remove('is-success');
	document.getElementById('heroStatus').classList.add('is-danger');

	document.getElementById('heroExplainer').innerHTML = 'API Wrapper Test Failed, reason: ' + reason;
}

function _testPageWarn(reason) {
	document.getElementById('heroStatus').classList.remove('is-success');
	document.getElementById('heroStatus').classList.add('is-warning');

	document.getElementById('heroExplainer').innerHTML = 'API Wrapper Test Warning, reason: ' + reason;
}

function getServerVersion() {
	return _api.get('version');
}

function createEntry(tableName, data) {
	invalidateCache(tableName);
	return _api.post(tableName, data);
}

function invalidateCache(tableName) {
	const keyDesc = `desc:${tableName}`;
	const keyTime = `${keyDesc}:time`;
	const keyTTL = `${keyDesc}:ttl`;

	localStorage.removeItem(keyDesc);
	localStorage.removeItem(keyTime);
	localStorage.removeItem(keyTTL);
}