import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import Config from '@ioc:Adonis/Core/Config';
import Database from '@ioc:Adonis/Lucid/Database';
import User from 'App/Models/User';
import { Exception } from '@adonisjs/core/build/standalone';

const permissionTable = Config.get('rolePermission.permission_table', 'permissions');
const rolePermissionTable = Config.get('rolePermission.role_permission_table', 'role_has_permissions');
const roleTable = Config.get('rolePermission.role_table', 'roles');
const userRoleTable = Config.get('rolePermission.user_role_table', 'link_accounts_roles');

/**
 * Permission authentication to check if user has any of the specified permissions
 *
 * Should be called after auth middleware
 */
export default class Can {
	/**
	 * Handle request
	 */
	public async handle(
		{ auth, response }: HttpContextContract,
		next: () => Promise<void>,
		permissionNames: string[]
	) {
		/**
		 * Check if user is logged-in
		 */
		let user = await auth.user;
		if (!user) {
			return response.unauthorized({ error: 'Must be logged in' });
		}
		let hasPermission = await this.checkHasPermissions(user, permissionNames);
		if (!hasPermission) {
			// return response.unauthorized({
			// 	error: `Doesn't have required role(s): ${permissionNames.join(',')}`,
			// });
			throw new Exception(`Doesn't have required permission(s): ${permissionNames.join(',')}`, 401);
		}
		await next();
	}

	private async checkHasPermissions(user: User, permissionNames: Array<string>): Promise<boolean> {
		let rolePlaceHolder = '(';
		let placeholders = new Array(permissionNames.length).fill('?');
		rolePlaceHolder += placeholders.join(',');
		rolePlaceHolder += ')';		

		// let test = user
		// 	.related('roles')
		// 	.query()
		// 	.count('permissions.name')
		// 	.innerJoin('gba.role_has_permissions', function () {
		// 		this.on('gba.role_has_permissions.role_id', 'roles.id');
		// 	})
		// 	.innerJoin('gba.permissions', function () {
		// 		this.on('role_has_permissions.permission_id', 'permissions.id');
		// 	})
		// 	.andWhereIn('permissions.name', permissionNames);
        
        // select "permissions"."name"
        // from "gba"."roles" 
        // inner join "gba"."link_accounts_roles" on "roles"."id" = "link_accounts_roles"."role_id" 
        // inner join "gba"."role_has_permissions" on "gba"."role_has_permissions"."role_id" = "roles"."id" 
        // inner join "gba"."permissions" on "role_has_permissions"."permission_id" = "permissions"."id" 
        // where ("permissions"."name" in ('dataset-list', 'dataset-publish')) 
        // and ("link_accounts_roles"."account_id" = 1)

		let {
			rows: {
				0: { permissioncount },
			},
		} = await Database.rawQuery(
			'SELECT count("p"."name") as permissionCount FROM ' + roleTable + 
            ' r INNER JOIN ' + 	userRoleTable + ' ur ON ur.role_id=r.id AND "ur"."account_id"=? ' +
			' INNER JOIN ' + 	rolePermissionTable + ' rp ON rp.role_id=r.id ' +	
            ' INNER JOIN ' + 	permissionTable + ' p ON rp.permission_id=p.id AND "p"."name" in ' +
            rolePlaceHolder +
				' LIMIT 1',
			[user.id, ...permissionNames]
		);

		return permissioncount > 0;
	}
}
