import os from 'node:os';
import type http from 'node:http';
import https from 'node:https';
import type { AstroIntegrationLogger } from 'astro';
import type { Options } from './types.js';
import type { AddressInfo } from 'node:net';

export async function logListeningOn(
	logger: AstroIntegrationLogger,
	server: http.Server | https.Server,
	options: Pick<Options, 'host'>
) {
	await new Promise<void>((resolve) => server.once('listening', resolve));
	const protocol = server instanceof https.Server ? 'https' : 'http';
	// Allow to provide host value at runtime
	const host = getResolvedHostForHttpServer(
		process.env.HOST !== undefined && process.env.HOST !== '' ? process.env.HOST : options.host
	);
	const { port } = server.address() as AddressInfo;
	const address = getNetworkAddress(protocol, host, port);

	if (host === undefined) {
		logger.info(
			`Server listening on \n  local: ${address.local[0]} \t\n  network: ${address.network[0]}\n`
		);
	} else {
		logger.info(`Server listening on ${address.local[0]}`);
	}
}

function getResolvedHostForHttpServer(host: string | boolean) {
	if (host === false) {
		// Use a secure default
		return 'localhost';
	} else if (host === true) {
		// If passed --host in the CLI without arguments
		return undefined; // undefined typically means 0.0.0.0 or :: (listen on all IPs)
	} else {
		return host;
	}
}

interface NetworkAddressOpt {
	local: string[];
	network: string[];
}

const wildcardHosts = new Set(['0.0.0.0', '::', '0000:0000:0000:0000:0000:0000:0000:0000']);

// this code from vite https://github.com/vitejs/vite/blob/d09bbd093a4b893e78f0bbff5b17c7cf7821f403/packages/vite/src/node/utils.ts#L892-L914
export function getNetworkAddress(
	protocol: 'http' | 'https' = 'http',
	hostname: string | undefined,
	port: number,
	base?: string
) {
	const NetworkAddress: NetworkAddressOpt = {
		local: [],
		network: [],
	};
	Object.values(os.networkInterfaces())
		.flatMap((nInterface) => nInterface ?? [])
		.filter(
			(detail) =>
				detail &&
				detail.address &&
				(detail.family === 'IPv4' ||
					// @ts-expect-error Node 18.0 - 18.3 returns number
					detail.family === 4)
		)
		.forEach((detail) => {
			let host = detail.address.replace(
				'127.0.0.1',
				hostname === undefined || wildcardHosts.has(hostname) ? 'localhost' : hostname
			);
			// ipv6 host
			if (host.includes(':')) {
				host = `[${host}]`;
			}
			const url = `${protocol}://${host}:${port}${base ? base : ''}`;
			if (detail.address.includes('127.0.0.1')) {
				NetworkAddress.local.push(url);
			} else {
				NetworkAddress.network.push(url);
			}
		});
	return NetworkAddress;
}
