import { camelize, decamelize } from '@ridi/object-case-converter'

export function toSnakeCase(payload: any): unknown {
	return decamelize(payload, {
		recursive: true,
		exception: {
			street1: 'street1',
			street2: 'street2',
			statusCode: 'statusCode',
			privateKey: 'privateKey', // TODO: Remove this when bc service schema is fixed
		},
	})
}

export function toCamelCase(payload: any): unknown {
	return camelize(payload, {
		recursive: { excludes: ['permissions'] }, // Getting the users permissions contains org names as keys, so we don't want to convert those to snake case
	})
}

function isValidDate(date: any): date is Date {
	return (
		date &&
		Object.prototype.toString.call(date) === '[object Date]' &&
		!isNaN(date)
	)
}

/**
 * Converts all `Date` objects to the ISO8601 formatted strings
 */
export function stringifyDates(obj: any): any {
	if (obj == null) return undefined

	if (typeof obj !== 'object') return obj

	if (Array.isArray(obj)) {
		return obj.map((val) => stringifyDates(val))
	}

	const result: any = {}
	Object.entries(obj ?? {}).forEach(([key, val]) => {
		if (isValidDate(val)) {
			result[key] = val.toISOString()
		} else if (val !== null && typeof val === 'object') {
			result[key] = stringifyDates(val)
		} else {
			result[key] = val
		}
	})
	return result
}

/**
 * Converts an object's string dates to `Date` objects
 */
export function parseDates<T = any>(obj: any): T {
	const res = recursiveDateParser(obj)
	return res as T
}

function recursiveDateParser(obj: any): any {
	if (obj == null) return undefined

	// Don't process primitives
	if (typeof obj !== 'object') return obj

	// Handle arrays
	if (Array.isArray(obj)) {
		return obj.map((val) => recursiveDateParser(val))
	}

	const result: any = {}
	Object.entries(obj ?? {}).forEach(([key, val]) => {
		if (val !== null && typeof val === 'object') {
			// Recursively parse sub-objects
			result[key] = recursiveDateParser(val)
		} else if (
			typeof val === 'string' &&
			isDateFormatted(val) &&
			isValidDate(new Date(val))
		) {
			// If the string is a valid date, parse it
			result[key] = new Date(val)
		} else {
			result[key] = val
		}
	})

	return result
}

export function isDateFormatted(str: string) {
	return str.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.(\d{3}))?/)
}
