//@ts-check

import * as schemas from "./{{name}}.schema.js";
import { validate, checkRole } from "../middlewares";
import roles from "./_roles.js"

/**
 * @typedef {Object} State
 */

export default class {
  /**
   * Bind service to router
   *
   * @param {import("koa-router")} router the koa compatible router
   * @returns {this}
   */
  bind(router) {
  {{#each operations}}
    const {{decapitalize name}} = async ctx => {
      {{#or parameters requestBody}}
      const req = {
        {{#each parameters}}
        {{#is in "path"}}
        {{name}}: ctx.params.{{name}},
        {{/is}}
        {{/each}}
        {{#withParamQuery parameters}}
        query: ctx.query,
        {{/withParamQuery}}
        {{#withParamHeader parameters}}
        headers: {
          {{#each parameters}}
          {{#is in "header"}}
          '{{name}}': ctx.header["{{lowercase name}}"],
          {{/is}}
          {{/each}}
        },
        {{/withParamHeader}}
        {{#withParamCookie parameters}}
        cookies: {
          {{#each parameters}}
          {{#is in "cookie"}}
          {{name}}: ctx.cookies.get({{name}},
          {{/is}}
          {{/each}}
        },
        {{/withParamCookie}}
        {{#with requestBody}}
        body: ctx.request.body,
        {{/with}} 
      };
      {{/or}}
      {{#or response.content response.headers}}const res = {{/or}}await this.{{decapitalize name}}({{#or parameters requestBody}}req,{{/or}}ctx);
      {{#if response.content}}
      ctx.body = res.body;
      {{/if}}
      {{#each response.headers}}
      ctx.set("{{lowercase @key}}", res.headers["{{lowercase @key}}"]);
      {{/each}}
      ctx.status = {{response.status}};
    };

  {{/each}}
  {{#each operations}}
    router.{{method}}(
      "{{toRoute path}}",
      validate({{#if requestSchema}}schemas.{{decapitalize name}}ReqSchema{{else}}null{{/if}}{{#if responseSchema}}, schemas.{{decapitalize name}}ResSchema{{/if}}),
      ...this.finalMiddlewares("{{name}}"),
      {{name}}
    );
  {{/each}}

    return this;
  }

  /**
   * implement following abstract methods in the inherited class
   */

  /**
   * Ability to inject some middlewares
   *
   * @abstract
   * @param {string} operation name of operation
   * @returns {Array<import("koa").Middleware<State>>} middlewares
   */
  middlewares(operation) {
    return [];
  }

  /**
   * final middleware
   *
   * @abstract
   * @param {string} operation name of operation
   * @returns {Array<import("koa").Middleware<State>>} middlewares
   */
  finalMiddlewares(operation) {
    const mws = this.middlewares(operation);
    const cr = checkRole(operation, roles);
    return mws ? mws.concat([cr]) : [cr];
  }
{{#each operations}}

  /**
   * {{summary}}
   *
   * @abstract
   {{#or parameters requestBody}}
   * @param {import("../api/{{../name}}").{{~capitalize name}}Request} req {{name}} request
   {{/or}}
   * @param {import("../api/{{../name}}").Context<State>} [ctx] koa context
   {{#or response.content response.headers}}
   * @returns {Promise<import("../api/{{../name}}").{{~capitalize name}}Response>} {{response.description}}
   {{/or}}
   */
  {{decapitalize name}}({{#or parameters requestBody}}req,{{/or}}ctx) {
    throw new Error("not implemented");
  }
{{/each}}
}
