import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import type { TimeRemaining } from './types';

export function cn(...inputs: ClassValue[]) {
	return twMerge(clsx(inputs));
}

// https://perryjanssen.medium.com/getting-random-tracks-using-the-spotify-api-61889b0c0c27
export function getRandomSearch() {
	// A list of all characters that can be chosen.
	const characters = 'abcdefghijklmnopqrstuvwxyz';

	// Gets a random character from the characters string.
	const randomCharacter = characters.charAt(Math.floor(Math.random() * characters.length));
	let randomSearch = '';

	// Places the wildcard character at the beginning, or both beginning and end, randomly.
	switch (Math.round(Math.random())) {
		case 0:
			randomSearch = randomCharacter + '%';
			break;
		case 1:
			randomSearch = '%' + randomCharacter + '%';
			break;
	}

	return randomSearch;
}

// Created using Claude 3.5 Sonett
export function shuffleObjectValues<T extends object>(arr: Array<T>): Array<T> {
	// Create a copy of the array
	const copy = structuredClone(arr);

	// Get all keys from the first object
	const keys = Object.keys(copy[0] as object);

	keys.forEach((key: string) => {
		// Get all values for this key with proper type assertion
		const values = copy.map((obj) => (obj as { [key: string]: unknown })[key]);

		// Fisher-Yates shuffle algorithm
		for (let i = values.length - 1; i > 0; i--) {
			const j = Math.floor(Math.random() * (i + 1));
			[values[i], values[j]] = [values[j], values[i]];
		}

		// Reassign shuffled values back to objects
		copy.forEach((obj, index) => {
			(obj as { [key: string]: unknown })[key] = values[index];
		});
	});

	return copy;
}

// https://stackoverflow.com/a/12646864
export function shuffleArray<T>(array: T[]): T[] {
	for (let i = array.length - 1; i >= 0; i--) {
		const j = Math.floor(Math.random() * (i + 1));
		[array[i], array[j]] = [array[j], array[i]];
	}

	return array;
}

// Created using Claude 3.5 Sonett
export function truncate(text: string, maxLength: number): string {
	// Return original text if it's shorter than or equal to maxLength
	if (text.length <= maxLength) {
		return text;
	}

	const ellipsis = '…';
	const tolerance = 5;
	const targetLength = maxLength - 1; // Account for ellipsis

	// Look for spaces within the tolerance range before targetLength
	let spaceBeforeIdx = -1;
	for (let i = targetLength; i >= targetLength - tolerance; i--) {
		if (text[i] === ' ') {
			spaceBeforeIdx = i;
			break;
		}
	}

	// Look for spaces within the tolerance range after targetLength
	let spaceAfterIdx = -1;
	for (let i = targetLength; i <= targetLength + tolerance && i < text.length; i++) {
		if (text[i] === ' ') {
			spaceAfterIdx = i;
			break;
		}
	}

	// Determine the best cutoff point
	let cutoffIndex = targetLength;
	if (spaceBeforeIdx !== -1 && spaceAfterIdx !== -1) {
		// If we found spaces both before and after, use the closest one
		cutoffIndex =
			targetLength - spaceBeforeIdx <= spaceAfterIdx - targetLength
				? spaceBeforeIdx
				: spaceAfterIdx;
	} else if (spaceBeforeIdx !== -1) {
		cutoffIndex = spaceBeforeIdx;
	} else if (spaceAfterIdx !== -1) {
		cutoffIndex = spaceAfterIdx;
	}

	return text.slice(0, cutoffIndex).trim() + ellipsis;
}

// Created using Claude 3.5 Sonett
export function getTimeRemaining(currentTime: Date, targetTime: Date): TimeRemaining {
	// Convert both dates to milliseconds and get the difference
	let delta = targetTime.getTime() - currentTime.getTime();

	// If target is in the past, return all zeros
	if (delta < 0) {
		return {
			years: 0,
			months: 0,
			days: 0,
			hours: 0,
			minutes: 0,
			seconds: 0
		};
	}

	// Calculate each unit
	const millisecondsPerSecond = 1000;
	const millisecondsPerMinute = millisecondsPerSecond * 60;
	const millisecondsPerHour = millisecondsPerMinute * 60;
	const millisecondsPerDay = millisecondsPerHour * 24;
	const millisecondsPerMonth = millisecondsPerDay * 30.436875; // Average month length
	const millisecondsPerYear = millisecondsPerDay * 365.2425; // Average year length

	const years = Math.floor(delta / millisecondsPerYear);
	delta -= years * millisecondsPerYear;

	const months = Math.floor(delta / millisecondsPerMonth);
	delta -= months * millisecondsPerMonth;

	const days = Math.floor(delta / millisecondsPerDay);
	delta -= days * millisecondsPerDay;

	const hours = Math.floor(delta / millisecondsPerHour);
	delta -= hours * millisecondsPerHour;

	const minutes = Math.floor(delta / millisecondsPerMinute);
	delta -= minutes * millisecondsPerMinute;

	const seconds = Math.floor(delta / millisecondsPerSecond);

	return {
		years,
		months,
		days,
		hours,
		minutes,
		seconds
	};
}
