import autoBind from 'auto-bind';
import {GqlApiBuilder} from "./GqlApiBuilder";
import XcProcedure from "../common/XcProcedure";
import BaseProcedure from "../common/BaseProcedure";
import GqlBaseResolver from "./GqlBaseResolver";

export class GqlProcedureResolver
  extends BaseProcedure {

  private acls: { [aclName: string]: { [role: string]: boolean } };

  constructor(builder: GqlApiBuilder, functions: any[], procedures: any[], acls) {
    super();
    autoBind(this);
    this.builder = builder;
    this.functions = functions;
    this.procedures = procedures;
    this.acls = acls;
    this.xcProcedure = new XcProcedure(builder);
  }

  public fnHandler(name) {
    return (async (args) => {
      let body = [];
      try {
        body = JSON.parse(args.body);
      } catch (_e) {
      }
      const result = await this.xcProcedure.callFunction(name, body);
      return JSON.stringify(result, null, 2);
    }).bind(this)
  }

  private procHandler(name) {
    // @ts-ignore
    return (async (args) => {
      let body = [];
      try {
        body = JSON.parse(args.body);
      } catch (_e) {
      }
      const result = await this.xcProcedure.callProcedure(name, body);
      return JSON.stringify(result, null, 2);
    }).bind(this)
  }

  public mapResolvers(): any {
    const resolvers = {};

    if (this.functions) {
      for (const {function_name} of this.functions) {
        resolvers[`_${function_name}`] = this.fnHandler(function_name);
      }
    }
    if (this.procedures) {
      for (const {procedure_name} of this.procedures) {
        resolvers[`_${procedure_name}`] = this.procHandler(procedure_name);
      }
    }
    return GqlBaseResolver.applyMiddlewares([this.middleware], resolvers);

  }

  public getSchema() {
    if (!this.functions?.length && !this.procedures?.length) {
      return ''
    }
    let resolvers = `
type Mutation {
 `;
    if (this.functions) {
      for (const {function_name} of this.functions) {
        resolvers += `_${function_name}(body:String):String\r\n`;
      }
    }
    if (this.procedures) {
      for (const {procedure_name} of this.procedures) {
        resolvers += `_${procedure_name}(body:String):String\r\n`;
      }
    }
    return resolvers + `}\r\n`;
  }

  updateMiddlewareBody(_body: string) { }

  private async middleware(_args, {req}, info: any) {
    const roles = (req as any)?.session?.passport?.user?.roles ?? {
      guest: true
    };

    try {
      const allowed = Object.keys(roles).some(role => roles[role] && this.acls?.[info.fieldName.slice(1)]?.[role]);
      if (allowed) {
        // any additional rules can be made here
        return;
      } else {
        const msg = roles.guest ? `Access Denied : Please Login or Signup for a new account` : `Access Denied for this account`;
        throw new Error(msg);
      }
    } catch (e) {
      throw e
    }
  }
}


/**
 * @copyright Copyright (c) 2021, Xgene Cloud Ltd
 *
 * @author Naveen MR <oof1lab@gmail.com>
 * @author Pranav C Balan <pranavxc@gmail.com>
 *
 * @license GNU AGPL version 3 or any later version
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 */
