import { Static, TSchema } from "@sinclair/typebox";
import { Value } from "@sinclair/typebox/value";
import { FastifyInstance, FastifyRequest, HTTPMethods } from "fastify"
import { RouteOptions } from "fastify/types/route.js";
import { type WebSocket } from "@fastify/websocket";

type WebsocketConnection = Parameters<Defined<RouteOptions["wsHandler"]>>[0];

type URLString = string;

export type FirRouteInput<TPayloadSchema extends TSchema> = {
	payload: Static<TPayloadSchema>,
}

export type FirWebsocketInput<TPayloadSchema extends TSchema> = {
	socket: WebsocketConnection,
	req: FastifyRequest,
	payload: Static<TPayloadSchema>,
}

export type FirWebsocketHandler<TIn extends TSchema = TSchema> = {
	onMessage?(input: FirWebsocketInput<TIn>): void,
	onOpen?(input: {socket: WebSocket, req: FastifyRequest}): void,
	onClose?(input: {socket: WebSocket, req: FastifyRequest}): void,
	onError?(input: {socket: WebSocket, req: FastifyRequest, error: unknown}): void,
};

export type FirRouteOptions<TIn extends TSchema = TSchema, TOut extends TSchema = TSchema> = {
	method: HTTPMethods,
	url: URLString,
	payloadT: TIn,
	responseT?: TOut,
} & ({
	handler: (input: FirRouteInput<TIn>) => Static<TOut> | Promise<Static<TOut>>,
} | {
	websocket: FirWebsocketHandler<TIn>,
})

type Defined<T> = T extends undefined ? never : T;

export const attachRoute = <TIn extends TSchema, TOut extends TSchema>(server: FastifyInstance, routeOptions: FirRouteOptions<TIn, TOut>) => {
	const {
		method,
		url,
		payloadT,
	} = routeOptions;

	if ("websocket" in routeOptions) {
		console.log('SETTING UP WS');
		const {websocket} = routeOptions;
		server.register(async function(fastify: FastifyInstance) {
			fastify.get('/api/ws/room', { websocket: true }, (socket: WebSocket, req: FastifyRequest) => {
				websocket.onOpen && websocket.onOpen({socket, req});
				socket.on('message', (message: any) => {
					const payload = JSON.parse(message.toString());
					if (Value.Check(payloadT, payload)) {
						websocket.onMessage && websocket.onMessage({socket, payload, req});
					} else {
						throw new Error("Payload wrong shape.");
					}
				});
				socket.on('close', () => {
					websocket.onClose && websocket.onClose({socket, req});
				});
				socket.on('error', (error: any) => {
					websocket.onError && websocket.onError({socket, error, req});
				});
			})
		});
		return;

		// const {websocket} = routeOptions;
		// const augmentedWsHandler = (conn: Parameters<Defined<RouteOptions["wsHandler"]>>[0]) => {
			// console.log('HELLO');
			// conn.on("message", (message) => {
			// 	const payload = JSON.parse(message.toString());
			// 	if (Value.Check(payloadT, payload)) {
			// 		websocket({socket: conn, payload});
			// 	} else {
			// 		throw new Error("Payload wrong shape.");
			// 	}
			// });
		// }

		// return {
        //     method: 'GET', // WebSocket upgrades are GET requests
        //     url,
		// 	// websocket: true,
        //     wsHandler: augmentedWsHandler,
		// 	handler: (...args) => {
		// 		console.log('socket!');
		// 		const socket = args[0].socket.on("message", () => {
		// 			console.log("connected!");
		// 		})
		// 	},
        //     // handler: (request, reply) => {
        //     //     reply.code(405).send({ message: 'Method Not Allowed' }); // Handle non-WebSocket requests
        //     // }
        // }
	}

	const {handler} = routeOptions;
	const augmentedHandler = (request: Parameters<RouteOptions["handler"]>[0]) => {
		const {
			body,
			query,
		} = request;
		const payload = body ?? query;
		if (Value.Check(payloadT, payload)) {
			return handler({payload});
		} else {
			throw new Error("Payload wrong shape.");
		}
	}

	server.route({
		method,
		url,
		handler: augmentedHandler,
	});
}