import { Worker as WorkerThread, workerData } from 'worker_threads';
import type { RefShardingCoreClient } from 'src/core/coreClient';
import { ClusterClientData, PackageType } from '../types';
import { ClientRefType } from 'src/core/clusterClient';
import { ChildProcess } from 'child_process';

export function getInfo(): ClusterClientData {
	const clusterMode = process.env.CLUSTER_MANAGER_MODE;
	if (clusterMode !== 'worker' && clusterMode !== 'process') throw new Error('NO_CLUSTER_MANAGER_MODE | ClusterManager Mode is not defined in the environment variables.');

	let data: ClusterClientData;

	if (clusterMode === 'process') {
		const shardList = (process.env.SHARD_LIST || '')
			.replace(/\[|\]/g, '')
			.split(',')
			.map((value) => Number(value.trim()))
			.filter((value) => Number.isInteger(value) && value >= 0);

		data = {
			ShardList: shardList,
			TotalShards: Number(process.env.TOTAL_SHARDS),
			ClusterCount: Number(process.env.CLUSTER_COUNT),
			ClusterId: Number(process.env.CLUSTER),
			ClusterManagerMode: clusterMode,
			ClusterQueueMode: process.env.CLUSTER_QUEUE_MODE as 'auto' | 'manual',
			RespondToHeartbeatWhenNotReady: process.env.RESPOND_TO_HEARTBEAT_WHEN_NOT_READY === 'true',
			FirstShardId: shardList[0] ?? 0,
			LastShardId: shardList[shardList.length - 1] ?? 0,
		};
	} else {
		const shardList = Array.isArray(workerData.SHARD_LIST) ? workerData.SHARD_LIST : [];
		const respondToHeartbeatWhenNotReady = workerData.RESPOND_TO_HEARTBEAT_WHEN_NOT_READY === true
			|| workerData.RESPOND_TO_HEARTBEAT_WHEN_NOT_READY === 'true';

		data = {
			ShardList: shardList,
			TotalShards: workerData.TOTAL_SHARDS,
			ClusterCount: workerData.CLUSTER_COUNT,
			ClusterId: workerData.CLUSTER,
			ClusterManagerMode: clusterMode,
			ClusterQueueMode: workerData.CLUSTER_QUEUE_MODE,
			RespondToHeartbeatWhenNotReady: respondToHeartbeatWhenNotReady,
			FirstShardId: shardList[0] ?? 0,
			LastShardId: shardList[shardList.length - 1] ?? 0,
		};
	}

	return data;
}

export async function getDiscordVersion(type: PackageType) {
	try {
		const { version } = await import(type);
		const [major = 0, minor = 0, patch = 0] = version.split('.').map(Number) as [number, number, number];

		return { major, minor, patch, raw: version };
	} catch (error) {
		throw new Error(`Failed to get version of ${type}: ${(error as Error).message}`);
	}
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function detectLibraryFromClient(client: any): PackageType | null {
	if (!client) return null;

	if (client.constructor?.name === 'Client' && 'guilds' in client && 'users' in client && 'channels' in client && typeof client.login === 'function' && !('api' in client)) {
		return 'discord.js';
	}

	if (client.constructor?.name === 'Client' && 'api' in client && 'rest' in client && 'gateway' in client && typeof client.api === 'object') {
		return '@discordjs/core';
	}

	if (client instanceof Object) {
		if ('guilds' in client && 'users' in client && !('api' in client)) return 'discord.js';
		if ('api' in client || (client.client && 'api' in client.client)) return '@discordjs/core';
	}

	return null;
}

export function isCoreClient(client: ClientRefType): client is RefShardingCoreClient {
	return detectLibraryFromClient(client) === '@discordjs/core';
}

export function isWorkerThread(process: ChildProcess | WorkerThread): process is WorkerThread {
	return 'threadId' in process;
}

export function isChildProcess(process: ChildProcess | WorkerThread): process is ChildProcess {
	return 'pid' in process;
}
