import { api, API_CLIENT } from '../utils/api.ts';
import * as asserts from '@std/assert';
import { USER } from '../models/user.ts';
import { delete_user, EPHEMERAL_SERVER, get_ephemeral_listen_server, get_new_user, random_username } from './helpers.ts';
import { Cookie, getSetCookies } from '@std/http/cookie';
import { encodeBase64 } from '@std/encoding';
import { generateTotp } from '../utils/totp.ts';

Deno.test({
	name: 'API - USERS - Login (password)',
	permissions: {
		env: true,
		read: true,
		write: true,
		net: true
	},
	fn: async () => {
		let test_server_info: EPHEMERAL_SERVER | null = null;
		try {
			test_server_info = await get_ephemeral_listen_server();
			const client: API_CLIENT = api({
				prefix: '/api',
				hostname: test_server_info.hostname,
				port: test_server_info.port
			});

			const info = await get_new_user(client, {
				password: 'password'
			});
			asserts.assert(info);

			const user = info.user;
			asserts.assert(user);

			let cookies: Cookie[] = [];
			const auth_response: any = await client.fetch('/auth', {
				method: 'POST',
				json: {
					username: user.username,
					password: 'password'
				},
				done: (response) => {
					cookies = getSetCookies(response.headers);
				}
			});

			const _authed_user: USER | undefined = auth_response.user;
			const authed_session: Record<string, any> | undefined = auth_response.session;

			cookies.push({
				name: 'totp',
				value: await generateTotp(authed_session?.secret ?? ''),
				maxAge: 30,
				expires: Date.now() + 30_000,
				path: '/'
			});

			const headers_for_get = new Headers();
			for (const cookie of cookies) {
				headers_for_get.append(`x-${cookie.name}`, cookie.value);
			}
			headers_for_get.append(
				'cookie',
				cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; ')
			);

			const retrieved_user: USER = await client.fetch(`/users/${user?.id}`, {
				headers: headers_for_get
			}) as USER;

			asserts.assertObjectMatch(retrieved_user, user);

			await delete_user(client, info);
		} finally {
			if (test_server_info) {
				await test_server_info?.server?.stop();
			}
		}
	}
});

Deno.test({
	name: 'API - USERS - Login (password_hash)',
	permissions: {
		env: true,
		read: true,
		write: true,
		net: true
	},
	fn: async () => {
		let test_server_info: EPHEMERAL_SERVER | null = null;
		try {
			test_server_info = await get_ephemeral_listen_server();
			const client: API_CLIENT = api({
				prefix: '/api',
				hostname: test_server_info.hostname,
				port: test_server_info.port
			});

			const password = 'hashed password!!!';
			const password_hash = encodeBase64(
				await crypto.subtle.digest('SHA-256', new TextEncoder().encode(password))
			);

			const info = await get_new_user(client, {
				password_hash
			});
			asserts.assert(info);

			const user = info.user;
			asserts.assert(user);

			const retrieved_user: USER = await client.fetch(`/users/${user?.id}`, {
				headers: info.headers
			}) as USER;

			asserts.assertObjectMatch(retrieved_user, user ?? {});

			await delete_user(client, info);
		} finally {
			if (test_server_info) {
				await test_server_info?.server?.stop();
			}
		}
	}
});
