// Parse kube-api path and get api-version, group, etc.

import type { KubeObject } from "./kube-object";
import { splitArray } from "../../common/utils";
import { apiManager } from "./api-manager";

export interface IKubeObjectRef {
  kind: string;
  apiVersion: string;
  name: string;
  namespace?: string;
}

export interface IKubeApiLinkRef {
  apiPrefix?: string;
  apiVersion: string;
  resource: string;
  name: string;
  namespace?: string;
}

export interface IKubeApiParsed extends IKubeApiLinkRef {
  apiBase: string;
  apiGroup: string;
  apiVersionWithGroup: string;
}

export function parseKubeApi(path: string): IKubeApiParsed {
  path = new URL(path, location.origin).pathname;
  const [, prefix, ...parts] = path.split("/");
  const apiPrefix = `/${prefix}`;
  const [left, right, namespaced] = splitArray(parts, "namespaces");
  let apiGroup, apiVersion, namespace, resource, name;

  if (namespaced) {
    switch (right.length) {
      case 1:
        name = right[0];
      // fallthrough
      case 0:
        resource = "namespaces"; // special case this due to `split` removing namespaces
        break;
      default:
        [namespace, resource, name] = right;
        break;
    }

    apiVersion = left.pop();
    apiGroup = left.join("/");
  } else {
    switch (left.length) {
      case 4:
        [apiGroup, apiVersion, resource, name] = left;
        break;
      case 2:
        resource = left.pop();
      // fallthrough
      case 1:
        apiVersion = left.pop();
        apiGroup = "";
        break;
      default:
      /**
       * Given that
       *  - `apiVersion` is `GROUP/VERSION` and
       *  - `VERSION` is `DNS_LABEL` which is /^[a-z0-9]((-[a-z0-9])|[a-z0-9])*$/i
       *     where length <= 63
       *  - `GROUP` is /^D(\.D)*$/ where D is `DNS_LABEL` and length <= 253
       *
       * There is no well defined selection from an array of items that were
       * separated by '/'
       *
       * Solution is to create a huristic. Namely:
       * 1. if '.' in left[0] then apiGroup <- left[0]
       * 2. if left[1] matches /^v[0-9]/ then apiGroup, apiVersion <- left[0], left[1]
       * 3. otherwise assume apiVersion <- left[0]
       * 4. always resource, name <- left[(0 or 1)+1..]
       */
        if (left[0].includes(".") || left[1].match(/^v[0-9]/)) {
          [apiGroup, apiVersion] = left;
          resource = left.slice(2).join("/");
        } else {
          apiGroup = "";
          apiVersion = left[0];
          [resource, name] = left.slice(1);
        }
        break;
    }
  }

  const apiVersionWithGroup = [apiGroup, apiVersion].filter(v => v).join("/");
  const apiBase = [apiPrefix, apiGroup, apiVersion, resource].filter(v => v).join("/");

  if (!apiBase) {
    throw new Error(`invalid apiPath: ${path}`);
  }

  return {
    apiBase,
    apiPrefix, apiGroup,
    apiVersion, apiVersionWithGroup,
    namespace, resource, name,
  };
}

export function createKubeApiURL(ref: IKubeApiLinkRef): string {
  const { apiPrefix = "/apis", resource, apiVersion, name } = ref;
  let { namespace } = ref;

  if (namespace) {
    namespace = `namespaces/${namespace}`;
  }

  return [apiPrefix, apiVersion, namespace, resource, name]
    .filter(v => v)
    .join("/");
}

export function lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): string {
  const {
    kind, apiVersion, name,
    namespace = parentObject.getNs()
  } = ref;

  if (!kind) return "";

  // search in registered apis by 'kind' & 'apiVersion'
  const api = apiManager.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);

  if (api) {
    return api.getUrl({ namespace, name });
  }

  // lookup api by generated resource link
  const apiPrefixes = ["/apis", "/api"];
  const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`;

  for (const apiPrefix of apiPrefixes) {
    const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });

    if (apiManager.getApi(apiLink)) {
      return apiLink;
    }
  }

  // resolve by kind only (hpa's might use refs to older versions of resources for example)
  const apiByKind = apiManager.getApi(api => api.kind === kind);

  if (apiByKind) {
    return apiByKind.getUrl({ name, namespace });
  }

  // otherwise generate link with default prefix
  // resource still might exists in k8s, but api is not registered in the app
  return createKubeApiURL({ apiVersion, name, namespace, resource });
}
