"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getKind = getKind;
exports.default = exports.doExplain = void 0;

var _debug = _interopRequireDefault(require("debug"));

var _core = require("@kui-shell/core");

var _flags = _interopRequireDefault(require("./flags"));

var _exec = require("./exec");

var _commandPrefix = _interopRequireDefault(require("../command-prefix"));

var _help = require("../../lib/util/help");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/*
 * Copyright 2019-2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
  function adopt(value) {
    return value instanceof P ? value : new P(function (resolve) {
      resolve(value);
    });
  }

  return new (P || (P = Promise))(function (resolve, reject) {
    function fulfilled(value) {
      try {
        step(generator.next(value));
      } catch (e) {
        reject(e);
      }
    }

    function rejected(value) {
      try {
        step(generator["throw"](value));
      } catch (e) {
        reject(e);
      }
    }

    function step(result) {
      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
    }

    step((generator = generator.apply(thisArg, _arguments || [])).next());
  });
};

const strings = (0, _core.i18n)('plugin-kubectl');
const debug = (0, _debug.default)('plugin-kubectl/controller/kubectl/explain');

function formatHref(href) {
  if (/api-conventions/.test(href) && !/sig-architecture/.test(href)) {
    return href.replace(/api-conventions/, 'sig-architecture/api-conventions');
  } else {
    return href;
  }
}

function formatDocumentation(description) {
  return description.replace(/\n/g, ' ').replace(/(More info:\s+)?(https:\/\/.*)/, (_, _2, href) => `\n\n[Consult official Kubernetes documentation](${formatHref(href)})`);
}

function formatField(cmdline, _) {
  return {
    mode: _[1],
    contentType: 'text/markdown',
    content: `### Type
${_[2]}
### Documentation
${formatDocumentation(_[4])}

[Explain this field](#kuiexec?command=${encodeURIComponent(cmdline + '.' + _[1])})
`
  };
}
/** alternate patterns to match against */


const kvd = /^KIND:\s+(\S+)\nVERSION:\s+(\S+)(\n\nRESOURCE:\s+(\S+).*)?\n\nDESCRIPTION:\n(\s*DEPRECATED - )?([\s\S]+)/;
const kvdf = /^KIND:\s+(\S+)\nVERSION:\s+(\S+)(\n\nRESOURCE:\s+(\S+).*)?\n\nDESCRIPTION:\n(\s*DEPRECATED - )?([\s\S]+)\n\nFIELDS:\n([\s\S]+)/;
const kvfd = /^KIND:\s+(\S+)\nVERSION:\s+(\S+)\n\nFIELD:\s+(\S+)\s+(.*)\n\nDESCRIPTION:\n(\s*DEPRECATED - )?([\s\S]+)/;

const doExplain = (command = 'kubectl') => function (args) {
  return __awaiter(this, void 0, void 0, function* () {
    if ((0, _help.isUsage)(args)) {
      // special case: get --help/-h
      return (0, _help.doHelp)(command, args);
    } // first, we do the raw exec of the given command


    const response = yield (0, _exec.doExecWithStdout)(args, undefined, command);

    try {
      // look first for a full Kind Version Description Fields;
      // otherwise, look for just Kind Version Description
      const match = response.match(kvdf) || response.match(kvd);
      const match2 = !match && response.match(kvfd);

      if (match || match2) {
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const kind = match ? match[1] : match2[1];
        const version = match ? match[2] : match2[2];
        const resource = match && match[4];
        const field = match2 && match2[3];
        const fieldType = match2 && match2[4];
        const isDeprecated = match ? match[5] : match2[5];
        const description = match ? match[6] : match2[6];
        const fields = match && match[7]; // const [_, kind, version, _2, resource, isDeprecated, description, fields] = match

        const fieldSections = !fields ? [] : fields.split(/\n\n/).filter(_ => _).map(_ => _.match(/\s*(\S+)\s+<(\S+)>( -required-)?\n\s*([\s\S]*)/)).filter(_ => _);
        const requiredFields = fieldSections.filter(_ => _[3]);
        const notRequiredFields = fieldSections.filter(_ => !_[3]);
        const topBreadcrumb = {
          label: 'API Resources',
          command: 'kubectl api-resources'
        };
        const apiGroup = version ? version.match(/^([^/]+)\//) : undefined;
        const apiGroupBreadcrumb = !apiGroup ? [] : [{
          label: apiGroup[1],
          command: `kubectl api-resources --api-group ${apiGroup[1]}`
        }];
        const resourceBreadcrumb = !resource ? [] : [{
          label: resource
        }];
        const fieldBreadcrumb = !match2 ? [] : [{
          label: field
        }];
        const format = formatField.bind(undefined, args.command);
        return {
          apiVersion: 'kui-shell/v1',
          kind: 'NavResponse',
          breadcrumbs: [topBreadcrumb].concat(apiGroupBreadcrumb).concat([{
            label: kind,
            command: resource || field ? `kubectl explain ${kind}` : ''
          }]).concat(resourceBreadcrumb).concat(fieldBreadcrumb),
          menus: [{
            label: resource || kind,
            items: [{
              mode: 'Overview',
              contentType: 'text/markdown',
              content: `### Description
#### ${description.replace(/\n/g, ' ')}
${match2 ? `### Type\n${fieldType.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\[/g, '&#91;').replace(/\]/g, '&#93;')}` : ''}
### Version
${version}
${isDeprecated ? `### Warnings\n${strings('This API Resource is deprecated')}` : ''}
    `
            }]
          }].concat(requiredFields.length === 0 ? [] : [{
            label: 'Required Fields',
            items: requiredFields.map(format)
          }]).concat(notRequiredFields.length === 0 ? [] : [{
            label: 'Fields',
            items: notRequiredFields.map(format)
          }])
        };
      }
    } catch (err) {
      console.error('error parsing explaing', err);
    }

    return response;
  });
};
/**
 * Cache of the getKind() lookup
 *
 */


exports.doExplain = doExplain;
const cache = {};
/**
 * @param kindAsProvidedByUser e.g. pod or po
 * @return e.g. Pod
 *
 */

function getKind(command, args, kindAsProvidedByUser) {
  return __awaiter(this, void 0, void 0, function* () {
    if (!cache[kindAsProvidedByUser]) {
      // otherwise, we need to do a more expensive call to `kubectl`
      // eslint-disable-next-line no-async-promise-executor
      cache[kindAsProvidedByUser] = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
        try {
          const ourArgs = Object.assign({}, args, {
            command: `${command} explain ${kindAsProvidedByUser}`
          });
          const explained = yield (0, _exec.doExecWithStdout)(ourArgs, undefined, command).catch(err => {
            // e.g. trying to explain CustomResourceDefinition.v1beta1.apiextensions.k8s.io
            // or a resource kind that does not exist
            debug(err);
            return `KIND: ${kindAsProvidedByUser}`;
          });
          const kindFromServer = explained.match(/^KIND:\s+(.*)/)[1];
          resolve(kindFromServer);
        } catch (err) {
          if (!/does not exist/i.test(err.message)) {
            console.error(`error explaining kind ${kindAsProvidedByUser}`, err);
            reject(err);
          }
        }
      }));
    }

    return cache[kindAsProvidedByUser];
  });
}

var _default = registrar => {
  const handler = doExplain();
  registrar.listen(`/${_commandPrefix.default}/kubectl/explain`, handler, _flags.default);
  registrar.listen(`/${_commandPrefix.default}/k/explain`, handler, _flags.default);
};

exports.default = _default;