import axios from 'axios';
import { Request, Response } from 'express';
import type { AxiosRequestConfig, Method } from 'axios';
import filterProperties from './helpers/filterProps';
import { IncomingMessage, ServerResponse } from 'http';


const stripForwardHeaders = (endpoint: string) =>
	endpoint.replace(/([?&])forwardHeaders=[^&]*([&]?)/g, (match, p1, p2) => (p1 === '?' ? '?' : p2));

const isValidIP = (endpoint: string) => {
	// Regular expression for matching IPv4 addresses
	const ipv4Pattern =
		/^.*(25[0-5]|(2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|(2[0-4][0-9]|[01]?[0-9][0-9]?)).*$/;
	// Regular expression for matching IPv6 addresses
	const ipv6Pattern =
		/^.*(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}(25[0-5]|(2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|(2[0-4][0-9]|[01]?[0-9][0-9]?))|([0-9a-fA-F]{1,4}:){1,4}:((([0-9a-fA-F]{1,4}:){1,4}|:)|([0-9a-fA-F]{1,4}:){1,4}:([0-9a-fA-F]{1,4}:){1,4})|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}(25[0-5]|(2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|(2[0-4][0-9]|[01]?[0-9][0-9]?))|([0-9a-fA-F]{1,4}:){1,4}:((([0-9a-fA-F]{1,4}:){1,4}|:)|([0-9a-fA-F]{1,4}:){1,4}:([0-9a-fA-F]{1,4}:){1,4})).*$/;

	return ipv4Pattern.test(endpoint) || ipv6Pattern.test(endpoint);
};

const isValidDomain = (endpoint: string) => {
	const whitelistedDomains = [
		'.com/frontend/cart/json',
		'.com/rest/dealerlocator/search',
		'moduslink.com',
		'.com/rest/emailcontactform',
		'.com/rest/productcomparison',
		'.com/rest/autosuggest',
		'.com/rest/search',
		'https://eshop-api-dev.oneux.bosch-pt.com',
		'https://eshop-api.bosch-diy.com',
		'https://eshop-api-preprod.bosch-diy.com',
		'https://eshop-api-stage.oneux.bosch-pt.com',
		'.com/rest/tag'
	];

	return whitelistedDomains.some((domain) => endpoint.includes(domain));
};

const refuseIpAccess = (req: Request, res: Response) => {
	const { query } = req;
	if (!isValidIP(query.endpoint as string)) return;
	res.status(403).send('Forbidden');
};

const refuseDomainAccess = (req: Request, res: Response) => {
	const { query } = req;
	if (isValidDomain(query.endpoint as string)) return;
	if (
		process.env.STRICT_REFUSE_DOMAIN_ACCESS &&
		process.env.STRICT_REFUSE_DOMAIN_ACCESS !== 'false'
	) {
		res.status(403).send('Forbidden');
	} else {
		// eslint-disable-next-line no-console
		console.error(
			`Endpoint ${query.endpoint} is not whitelisted and will be blocked in future versions`
		);
	}
};

const refuseAccess = (req: Request, res: Response) => {
	// forbid any attempt to acdess endpoints using IP
	refuseIpAccess(req, res);
	// refuse access to domains that are not whitelisted
	refuseDomainAccess(req, res);
};

// const allowedEnvs = ['development', 'test'];
// const getPublicHost = () => process.env.NEXT_PUBLIC_HOSTNAME?.replace(/^https?:\/\//i, '');
// const isLocalHostAllowed = (host: string) =>
// 	allowedEnvs.includes(process.env.NEXT_PUBLIC_NODE_ENV as string) && host === 'localhost:4000';
const corsHandler = async (req: Request, res: Response) => {
	// check if access should be allowed
	// refuseAccess(req, res);

	const { query, method, body } = req;
	// const { host } = req.headers;
	// console.log('>> logging all the headers', req.headers);
	// eslint-disable-next-line no-console

	// if (
	// 	!(
	// 		allowedEnvs.includes(process.env.NEXT_PUBLIC_NODE_ENV as string) ||
	// 		host === getPublicHost() ||
	// 		isLocalHostAllowed(host as string)
	// 	)
	// ) {
	// 	res.status(403).send('Forbidden');
	// }

	let { endpoint } = query;
	let forwardedHeaders: any[] = [];
	// const queryData = parseUrl(endpoint as string).query;
    const queryData = new URL(endpoint as string).searchParams; // URLSearchParams object for query parameters
	if (queryData.get('forwardHeaders')) {
		forwardedHeaders = (queryData.get('forwardHeaders') as string).split(',');
		endpoint = stripForwardHeaders(endpoint as string);
	}

	if (endpoint == null || endpoint === '') {
		const out = {
			error: 'Missing endpoint parameter!'
		};

		res.status(400).json(out);
		return;
	}

	if (!Number.isNaN(parseInt(<string>endpoint, 10)) || typeof endpoint !== 'string') {
		const out = {
			error: 'Endpoint parameter must be a string!'
		};

		res.status(400).json(out);
		return;
	}

	const h: any = req.headers;

	try {
		const reqConfig: AxiosRequestConfig = {
			data: body,
			headers: {
				'X-Requested-With': 'XMLHttpRequest'
			},
			method: (method as Method) || 'get',
			params: filterProperties({ ...req.query }, ['endpoint']),
			url: encodeURI(endpoint)
		};

		if (forwardedHeaders.length) {
			forwardedHeaders.forEach((header) => {
				reqConfig.headers = {
					...reqConfig.headers,
					[header]: h[header.toLowerCase()]
				};
			});
		}

		const endpointReq = await axios(reqConfig);
		const endpointRes = await endpointReq.data;

		res.status(200).json(endpointRes);
	} catch (err: any) {
		const out = {
			error: 'Something went wrong...',
			message: err.message
		};

		res.status(err.response?.status || 500).json(out);
	}
};

export default corsHandler;
