import { CodeEditor } from "../../../elements/CodeEditor";
import { Component, createRef, JSX } from "preact";
import { DefaultPageProps } from "../../../../types/DefaultPageProps";
import { SecretTitleElement } from "../SecretTitleElement";
import { setErrorText } from "../../../../pageUtils";
import { sortedObjectMap } from "../../../../utils";
import { InputWithTitle } from "../../../elements/InputWithTitle";
import Hjson from "hjson";
import JSON5 from "json5";
import yaml from "js-yaml";
import i18next from "i18next";

// todo: put this in settings
const defaultIndent = 2;

function parseData(data: string, syntax: string = "json"): Record<string, unknown> {
  if (syntax == "json") {
    return JSON.parse(data);
  } else if (syntax == "json5") {
    return JSON5.parse(data);
  } else if (syntax == "hjson") {
    return Hjson.parse(data);
  } else if (syntax == "yaml") {
    return yaml.load(data) as Record<string, unknown>;
  }
}

function dumpData(data: Record<string, unknown>, space: number = defaultIndent, syntax: string = "json"): string {
  if (syntax == "json") {
    return JSON.stringify(data, null, space);
  } else if (syntax == "json5") {
    return JSON5.stringify(data, null, space);
  } else if (syntax == "hjson") {
    return Hjson.stringify(data, { space: space });
  } else if (syntax == "yaml") {
    return yaml.dump(data, { indent: space });
  }
}

export function validateData(str: string, syntax: string = "json"): boolean {
  try {
    parseData(str, syntax);
  } catch (e) {
    console.log(e);
    return false;
  }
  return true;
}

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

type KVEditState =
  | {
    dataLoaded: false;
    syntax: string;
  }
  | {
    dataLoaded: true;
    kvData: Record<string, unknown>;
    code: string;
    syntax: string;
  };

export class KVEditor extends Component<KVEditProps, KVEditState> {
  constructor() {
    super();
    // TODO: add a global syntax option to settings for default
    this.state = {
      dataLoaded: false,
      syntax: "json",
    };
  }

  async editorSave(): Promise<void> {
    if (!this.state.dataLoaded) return;
    const editorContent = this.state.code;

    if (!validateData(editorContent, this.state.syntax)) {
      setErrorText(i18next.t("kv_sec_edit_invalid_json_err"));
      return;
    }

    await this.props.api.createOrUpdateSecret(
      this.props.baseMount,
      this.props.secretPath.map((e) => e + "/"),
      this.props.secretItem,
      parseData(editorContent, this.state.syntax),
    );
    window.history.back();
  }

  onCodeUpdate(code: string): void {
    this.setState({
      code: code,
    });
  }

  async loadData() {
    let kvData = await this.props.api.getSecret(
      this.props.baseMount,
      this.props.secretPath.map((e) => e + "/"),
      this.props.secretItem,
    )
    this.setState({
      dataLoaded: true,
      kvData: kvData,
      code: this.getStringKVData(kvData)
    });
  }

  async componentDidMount() {
    if (!this.state.dataLoaded) {
      await this.loadData();
    }
  }

  getStringKVData(data: Record<string, unknown>): string {
    return dumpData(Object.fromEntries(sortedObjectMap(data)), defaultIndent, this.state.syntax);
  }

  syntaxSelectRef = createRef<HTMLSelectElement>()

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

    let codeEditorLanguage: string;

    if (this.state.syntax == "json") {
      codeEditorLanguage = "json"
    } else if (this.state.syntax == "json5") {
      codeEditorLanguage = "json5"
    }else if (this.state.syntax == "hjson") {
      codeEditorLanguage = "js"
    } else if (this.state.syntax == "yaml") {
      codeEditorLanguage = "yaml"
    }

    return (
      <div>
        <InputWithTitle title={i18next.t("kv_sec_edit_syntax")}>
          <select ref={this.syntaxSelectRef} class="uk-select uk-form-width-medium" onChange={() => {
            this.setState({ syntax: this.syntaxSelectRef.current.value })

          }}>
            <option label="JSON" value="json" />
            <option label="JSON5" value="json5" />
            <option label="Hjson" value="hjson" />
            <option label="Yaml" value="yaml" />
          </select>
        </InputWithTitle>
        <p class="uk-text-danger" id="errorText" />
        <CodeEditor
          language={codeEditorLanguage}
          tabSize={defaultIndent}
          code={this.getStringKVData(this.state.kvData)}
          onUpdate={(code) => this.onCodeUpdate(code)}
        />
        <button class="uk-button uk-button-primary" onClick={() => this.editorSave()}>
          {i18next.t("common_edit")}
        </button>
      </div>
    );
  }
}

export class KeyValueEdit extends Component<DefaultPageProps> {
  render() {
    const baseMount = this.props.matches["baseMount"];
    const secretPath = this.props.matches["secretPath"].split("/");
    const item = this.props.matches["item"];

    return (
      <>
        <SecretTitleElement
          type="kv"
          baseMount={baseMount}
          secretPath={secretPath}
          item={this.props.matches["item"]}
          suffix={i18next.t("kv_sec_edit_suffix")}
        />
        <KVEditor
          settings={this.props.settings}
          api={this.props.api}
          baseMount={baseMount}
          secretPath={secretPath}
          secretItem={item}
        />
      </>
    );
  }
}