import { Button } from "../../../elements/Button";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { DoesNotExistError } from "../../../../types/internalErrors";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { TextInput } from "../../../elements/forms/TextInput";
import { delSecretsEngineURL, kvListURL, kvNewURL, kvViewURL } from "../../pageLinks";
import { route } from "preact-router";
import { sendErrorNotification } from "../../../elements/ErrorMessage";
import i18next from "i18next";

export type KVKeysListProps = DefaultPageProps & {
  baseMount: string;
  secretPath: string[];
};

type KVKeysListState = {
  dataLoaded: boolean;
  keys: string[];
  searchQuery: string;
};

function SecretsList(baseMount: string, secretPath: string[], secrets: string[]): JSX.Element[] {
  return secrets.map((secret) => (
    <li>
      <a
        onClick={async () => {
          if (secret.endsWith("/")) {
            route(kvListURL(baseMount, [...secretPath, secret.replace("/", "")]));
          } else {
            route(kvViewURL(baseMount, secretPath, secret));
          }
        }}
      >
        {secret}
      </a>
    </li>
  ));
}

export class KVKeysList extends Component<KVKeysListProps, KVKeysListState> {
  constructor() {
    super();
    this.state = {
      dataLoaded: false,
      keys: [],
      searchQuery: "",
    };
  }

  async componentDidMount() {
    try {
      await this.loadData();
    } catch (e: unknown) {
      const error = e as Error;
      sendErrorNotification(error.message);
    }
  }

  async loadData(): Promise<void> {
    try {
      const keys = await this.props.api.getSecrets(this.props.baseMount, this.props.secretPath);
      this.setState({
        dataLoaded: true,
        keys: keys,
      });
      return;
    } catch (e: unknown) {
      const error = e as Error;
      if (error == DoesNotExistError) {
        // getSecrets also 404's on no keys so dont go all the way back.
        if (this.props.secretPath.length != 0) {
          window.history.back();
          return;
        }
      } else {
        throw error;
      }

      this.setState({
        dataLoaded: true,
        keys: null,
      });
    }
  }

  componentDidUpdate(prevProps: KVKeysListProps): void {
    if (
      prevProps.baseMount !== this.props.baseMount ||
      prevProps.secretPath !== this.props.secretPath
    ) {
      this.setState({
        dataLoaded: false,
      });
      void this.loadData();
    }
  }

  searchBarRef = createRef<HTMLInputElement>();

  render(): JSX.Element {
    if (!this.state.dataLoaded) {
      return <p>{i18next.t("content_loading")}</p>;
    }

    if (this.state.keys == null) {
      return <p>{i18next.t("kv_view_none_here_text")}</p>;
    }

    return (
      <>
        <Margin>
          <TextInput
            inputRef={this.searchBarRef}
            name="path"
            placeholder={i18next.t("kv_view_search_input_text")}
            onInput={async () => {
              this.setState({
                searchQuery: this.searchBarRef.current.value,
              });
            }}
          />
        </Margin>

        <Margin>
          <ul class="uk-nav">
            {...((): JSX.Element[] => {
              let secrets: string[] = this.state.keys;
              if (this.state.searchQuery.length > 0) {
                secrets = secrets.filter((secret) => secret.includes(this.state.searchQuery));
              }
              return SecretsList(this.props.baseMount, this.props.secretPath, secrets);
            })()}
          </ul>
        </Margin>
      </>
    );
  }
}

type KeyValueListState = {
  pathCaps: string[];
  mountCaps: string[];
  mountType: string;
};

export class KeyValueList extends Component<DefaultPageProps, KeyValueListState> {
  async componentDidMount() {
    const baseMount = this.props.matches["baseMount"];
    const secretPath = this.props.matches["secretPath"].split("/").filter((e) => e.length > 0);

    const mountsPath = "/sys/mounts/" + baseMount;
    const currentPath = baseMount + secretPath.join();

    try {
      const caps = await this.props.api.getCapabilitiesPath([mountsPath, currentPath]);
      const mount = await this.props.api.getMount(baseMount);

      this.setState({
        mountCaps: caps[mountsPath],
        pathCaps: caps[currentPath],
        mountType: mount.type,
      });
    } catch (e: unknown) {
      const error = e as Error;
      sendErrorNotification(error.message);
    }
  }

  render() {
    if (!this.state.pathCaps) return;

    const baseMount = this.props.matches["baseMount"];
    const secretPath = this.props.matches["secretPath"].split("/");

    return (
      <>
        <SecretTitleElement
          type="kv"
          baseMount={baseMount}
          secretPath={secretPath}
          item={this.props.matches["item"]}
        />
        <p>
          {this.state.pathCaps.includes("create") && (
            <Button
              text={i18next.t("kv_view_new_btn")}
              color="primary"
              route={kvNewURL(baseMount, secretPath)}
            />
          )}
          {secretPath.length == 0 && this.state.mountCaps.includes("delete") && (
            <Button
              text={i18next.t("kv_view_delete_btn")}
              color="danger"
              route={delSecretsEngineURL(baseMount)}
            />
          )}
        </p>
        {this.state.mountType == "cubbyhole" && <p>{i18next.t("kv_view_cubbyhole_text")}</p>}
        <KVKeysList
          settings={this.props.settings}
          api={this.props.api}
          baseMount={baseMount}
          secretPath={secretPath}
        />
      </>
    );
  }
}