/**
 * Copyright (c) HashiCorp, Inc.
 * SPDX-License-Identifier: BUSL-1.1
 */

import { dasherize } from '@ember/string';
import { singularize } from 'ember-inflector';

// TODO: Consolidate with openapi-to-attrs once it's typescript

interface Path {
  path: string;
  itemType: string;
  itemName: string;
  operations: string[];
  action: string;
  navigation: boolean;
  param: string | false;
}
interface PathsInfo {
  apiPath: string;
  itemType: string;
  itemTypes: string[];
  paths: Path[];
}

interface OpenApiParameter {
  description?: string;
  in: string;
  name: string;
  required: boolean;
  schema: object;
}
interface DisplayAttrs {
  itemType: string;
  action: string;
  navigation?: boolean;
  description?: string;
  name?: string;
  group?: string;
  value?: string | number;
  sensitive?: boolean;
}
interface OpenApiAction {
  parameters: Array<{ name: string }>;
}
interface OpenApiPath {
  description?: string;
  parameters: OpenApiParameter[];
  'x-vault-displayAttrs': DisplayAttrs;
  get?: OpenApiAction;
  post?: OpenApiAction;
  delete?: OpenApiAction;
}

// Take object entries from the OpenAPI response and consolidate them into an object which includes itemTypes, operations, and paths
export function reducePathsByPathName(pathsInfo: PathsInfo, currentPath: [string, OpenApiPath]): PathsInfo {
  const pathName = currentPath[0];
  const pathDetails = currentPath[1];
  const displayAttrs = pathDetails['x-vault-displayAttrs'];
  if (!displayAttrs) {
    // don't include paths that don't have display attrs
    return pathsInfo;
  }

  let itemType, itemName;
  if (displayAttrs.itemType) {
    itemType = displayAttrs.itemType;
    let items = itemType.split(':');
    itemName = items[items.length - 1];
    items = items.map((item) => dasherize(singularize(item.toLowerCase())));
    itemType = items.join('~*');
  }

  if (itemType && !pathsInfo.itemTypes.includes(itemType)) {
    pathsInfo.itemTypes.push(itemType);
  }

  const operations = [];
  if (pathDetails.get) {
    operations.push('get');
  }
  if (pathDetails.post) {
    operations.push('post');
  }
  if (pathDetails.delete) {
    operations.push('delete');
  }
  if (pathDetails.get && pathDetails.get.parameters && pathDetails.get.parameters[0]?.name === 'list') {
    operations.push('list');
  }

  pathsInfo.paths.push({
    path: pathName,
    itemType: itemType || displayAttrs.itemType,
    itemName: itemName || pathsInfo.itemType || displayAttrs.itemType,
    operations,
    action: displayAttrs.action,
    navigation: displayAttrs.navigation === true,
    param: _getPathParam(pathName),
  });

  return pathsInfo;
}

const apiPathRegex = new RegExp(/\{\w+\}/, 'g');

/**
 * getPathParam takes an OpenAPI url and returns the first path param name, if it exists.
 * This is an internal method, but exported for testing.
 */
export function _getPathParam(pathName: string): string | false {
  if (!pathName) return false;
  const params = pathName.match(apiPathRegex);
  // returns array like ['{username}'] or null
  if (!params) return false;
  // strip curly brackets from param name
  // previous behavior only returned the first param, so we match that for now
  return params[0]?.replace(new RegExp('{|}', 'g'), '') || false;
}

export function pathToHelpUrlSegment(path: string): string {
  if (!path) return '';
  return path.replaceAll(apiPathRegex, 'example');
}

export function filterPathsByItemType(pathInfo: PathsInfo, itemType: string): Path[] {
  if (!itemType) {
    return pathInfo.paths;
  }
  return pathInfo.paths.filter((path) => {
    return itemType === path.itemType;
  });
}

/**
 * This object maps model names to the openAPI path that hydrates the model, given the backend path.
 */
const OPENAPI_POWERED_MODELS = {
  'auth-config/azure': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/cert': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/gcp': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/github': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/jwt': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/kubernetes': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/ldap': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/oidc': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/okta': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'auth-config/radius': (backend: string) => `/v1/auth/${backend}/config?help=1`,
  'kmip/config': (backend: string) => `/v1/${backend}/config?help=1`,
  'kmip/role': (backend: string) => `/v1/${backend}/scope/example/role/example?help=1`,
  'pki/certificate/generate': (backend: string) => `/v1/${backend}/issue/example?help=1`,
  'pki/certificate/sign': (backend: string) => `/v1/${backend}/sign/example?help=1`,
  'pki/config/acme': (backend: string) => `/v1/${backend}/config/acme?help=1`,
  'pki/config/cluster': (backend: string) => `/v1/${backend}/config/cluster?help=1`,
  'pki/config/urls': (backend: string) => `/v1/${backend}/config/urls?help=1`,
  'pki/role': (backend: string) => `/v1/${backend}/roles/example?help=1`,
  'pki/sign-intermediate': (backend: string) => `/v1/${backend}/issuer/example/sign-intermediate?help=1`,
  'pki/tidy': (backend: string) => `/v1/${backend}/config/auto-tidy?help=1`,
  'role-ssh': (backend: string) => `/v1/${backend}/roles/example?help=1`,
};

export function getHelpUrlForModel(modelType: string, backend: string) {
  const urlFn = OPENAPI_POWERED_MODELS[modelType as keyof typeof OPENAPI_POWERED_MODELS] as (
    backend: string
  ) => string;
  if (!urlFn) return null;
  return urlFn(backend);
}
