import { Button } from "../../../elements/Button";
import { Component, JSX, createRef } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { DoesNotExistError } from "../../../../types/internalErrors";
import { InlineButtonBox } from "../../../elements/InlineButtonBox";
import { Margin } from "../../../elements/Margin";
import { SecretTitleElement } from "../SecretTitleElement";
import { TextInput } from "../../../elements/forms/TextInput";
import { combineKVPath, splitKVPath } from "./kvPathUtils";
import { delSecretsEngineURL, kvListURL, kvNewURL, kvViewURL } from "../../pageLinks";
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) => {
    let link = "";
    const combined = combineKVPath(secretPath, secret);
    if (secret.endsWith("/")) {
      link = kvListURL(baseMount, [...combined.secretPath, combined.secretItem]);
    } else {
      link = kvViewURL(baseMount, combined.secretPath, combined.secretItem);
    }

    return (
      <li>
        <a href={link}>{secret}</a>
      </li>
    );
  });
}

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

  async componentDidMount() {
    this.setState({
      searchQuery: "",
    });

    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,
        searchQuery: "",
      });
      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 = splitKVPath(this.props.matches["secretPath"]);

    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 = splitKVPath(this.props.matches["secretPath"]);

    return (
      <>
        <SecretTitleElement
          type="kv"
          baseMount={baseMount}
          secretPath={secretPath}
          item={this.props.matches["item"]}
        />
        <InlineButtonBox>
          {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)}
            />
          )}
        </InlineButtonBox>
        {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}
        />
      </>
    );
  }
}