import { v4 as uuidv4 } from 'uuid';
import sha256 from 'js-sha256';

//////////////////////////
// Helper functions
//////////////////////////

export const splitStream = (splitOn) => {
	let buffer = '';
	return new TransformStream({
		transform(chunk, controller) {
			buffer += chunk;
			const parts = buffer.split(splitOn);
			parts.slice(0, -1).forEach((part) => controller.enqueue(part));
			buffer = parts[parts.length - 1];
		},
		flush(controller) {
			if (buffer) controller.enqueue(buffer);
		}
	});
};

export const convertMessagesToHistory = (messages) => {
	const history = {
		messages: {},
		currentId: null
	};

	let parentMessageId = null;
	let messageId = null;

	for (const message of messages) {
		messageId = uuidv4();

		if (parentMessageId !== null) {
			history.messages[parentMessageId].childrenIds = [
				...history.messages[parentMessageId].childrenIds,
				messageId
			];
		}

		history.messages[messageId] = {
			...message,
			id: messageId,
			parentId: parentMessageId,
			childrenIds: []
		};

		parentMessageId = messageId;
	}

	history.currentId = messageId;
	return history;
};

export const getGravatarURL = (email) => {
	// Trim leading and trailing whitespace from
	// an email address and force all characters
	// to lower case
	const address = String(email).trim().toLowerCase();

	// Create a SHA256 hash of the final string
	const hash = sha256(address);

	// Grab the actual image URL
	return `https://www.gravatar.com/avatar/${hash}`;
};

export const copyToClipboard = (text) => {
	if (!navigator.clipboard) {
		const textArea = document.createElement('textarea');
		textArea.value = text;

		// Avoid scrolling to bottom
		textArea.style.top = '0';
		textArea.style.left = '0';
		textArea.style.position = 'fixed';

		document.body.appendChild(textArea);
		textArea.focus();
		textArea.select();

		try {
			const successful = document.execCommand('copy');
			const msg = successful ? 'successful' : 'unsuccessful';
			console.log('Fallback: Copying text command was ' + msg);
		} catch (err) {
			console.error('Fallback: Oops, unable to copy', err);
		}

		document.body.removeChild(textArea);
		return;
	}
	navigator.clipboard.writeText(text).then(
		function () {
			console.log('Async: Copying to clipboard was successful!');
		},
		function (err) {
			console.error('Async: Could not copy text: ', err);
		}
	);
};

export const checkVersion = (required, current) => {
	// Returns true when current version is below required
	return current === '0.0.0'
		? false
		: current.localeCompare(required, undefined, {
				numeric: true,
				sensitivity: 'case',
				caseFirst: 'upper'
		  }) < 0;
};

export const findWordIndices = (text) => {
	const regex = /\[([^\]]+)\]/g;
	const matches = [];
	let match;

	while ((match = regex.exec(text)) !== null) {
		matches.push({
			word: match[1],
			startIndex: match.index,
			endIndex: regex.lastIndex - 1
		});
	}

	return matches;
};

export const removeFirstHashWord = (inputString) => {
	// Split the string into an array of words
	const words = inputString.split(' ');

	// Find the index of the first word that starts with #
	const index = words.findIndex((word) => word.startsWith('#'));

	// Remove the first word with #
	if (index !== -1) {
		words.splice(index, 1);
	}

	// Join the remaining words back into a string
	const resultString = words.join(' ');

	return resultString;
};

export const transformFileName = (fileName) => {
	// Convert to lowercase
	const lowerCaseFileName = fileName.toLowerCase();

	// Remove special characters using regular expression
	const sanitizedFileName = lowerCaseFileName.replace(/[^\w\s]/g, '');

	// Replace spaces with dashes
	const finalFileName = sanitizedFileName.replace(/\s+/g, '-');

	return finalFileName;
};

export const calculateSHA256 = async (file) => {
	// Create a FileReader to read the file asynchronously
	const reader = new FileReader();

	// Define a promise to handle the file reading
	const readFile = new Promise((resolve, reject) => {
		reader.onload = () => resolve(reader.result);
		reader.onerror = reject;
	});

	// Read the file as an ArrayBuffer
	reader.readAsArrayBuffer(file);

	try {
		// Wait for the FileReader to finish reading the file
		const buffer = await readFile;

		// Convert the ArrayBuffer to a Uint8Array
		const uint8Array = new Uint8Array(buffer);

		// Calculate the SHA-256 hash using Web Crypto API
		const hashBuffer = await crypto.subtle.digest('SHA-256', uint8Array);

		// Convert the hash to a hexadecimal string
		const hashArray = Array.from(new Uint8Array(hashBuffer));
		const hashHex = hashArray.map((byte) => byte.toString(16).padStart(2, '0')).join('');

		return `${hashHex}`;
	} catch (error) {
		console.error('Error calculating SHA-256 hash:', error);
		throw error;
	}
};

export const getImportOrigin = (_chats) => {
	// Check what external service chat imports are from
    if ('mapping' in _chats[0]) {
        return 'gpt';
    }
    return 'webui';
}

const convertGptMessages = (convo) => {
	// Parse OpenAI chat messages and create chat dictionary for creating new chats
    const mapping = convo["mapping"];
	const messages = [];
	let currentId = "";

    for (let message_id in mapping) {
        const message = mapping[message_id];
		currentId = message_id;
		if (message["message"] == null || message["message"]["content"]["parts"][0] == "") {
			// Skip chat messages with no content
			continue;
		} else {
			const new_chat = {
				"id": message_id,
				"parentId": messages.length > 0 ? message["parent"] : null,
				"childrenIds": message["children"] || [],
				"role": message["message"]?.["author"]?.["role"] !== "user" ? "assistant" : "user",
				"content": message["message"]?.["content"]?.['parts']?.[0] || "",
				"model": '',
				"done": true,
				"context": null,
			}
			messages.push(new_chat)
		}
    }

	let history = {};
	messages.forEach(obj => history[obj.id] = obj);

	const chat = {
		"history": {
			"currentId": currentId,
			"messages": history, // Need to convert this to not a list and instead a json object
		},
		"models": [""],
		"messages": messages,
		"options": {},
		"timestamp": convo["create_time"],
		"title": convo["title"],
	}
    return chat;
}

export const convertGptChats = (_chats) => {
	// Create a list of dictionaries with each conversation from import
    const chats = [];
    for (let convo of _chats) {
        const chat = {
			"id": convo["id"],
        	"user_id": '',
			"title": convo["title"],
			"chat": convertGptMessages(convo),
			"timestamp": convo["timestamp"],
    	}
		chats.push(chat)
	}
    return chats;
}
