import React from "react";
import classnames from "classnames";
import { type WithTranslation, withTranslation } from "react-i18next";

import { type EditorView } from "@codemirror/view";
import stringifyPretty from "json-stringify-pretty-compact";

import {createEditor} from "../libs/codemirror-mgl";
import type { StylePropertySpecification } from "maplibre-gl";

export type InputJsonProps = {
  value: object
  maxHeight?: number
  lineNumbers?: boolean
  lineWrapping?: boolean
  gutters?: string[]
  className?: string
  onChange(object: object): void
  onFocus?(...args: unknown[]): unknown
  onBlur?(...args: unknown[]): unknown
  onJSONValid?(): void
  onJSONInvalid?(_err: Error): void
  lintType: "layer" | "style" | "expression" | "json"
  spec?: StylePropertySpecification | undefined
};
type InputJsonInternalProps = InputJsonProps & WithTranslation;

type InputJsonState = {
  isEditing: boolean
  prevValue: string
};

class InputJsonInternal extends React.Component<InputJsonInternalProps, InputJsonState> {
  static defaultProps = {
    lineNumbers: true,
    lineWrapping: false,
    gutters: ["CodeMirror-lint-markers"],
    onFocus: () => {},
    onBlur: () => {},
    onJSONInvalid: () => {},
    onJSONValid: () => {},
  };
  _keyEvent: string;
  _doc: EditorView | undefined;
  _el: HTMLDivElement | null = null;
  _cancelNextChange: boolean = false;

  constructor(props: InputJsonInternalProps) {
    super(props);
    this._keyEvent = "keyboard";
    this.state = {
      isEditing: false,
      prevValue: this.getPrettyJson(this.props.value),
    };
  }

  getPrettyJson(data: any) {
    return stringifyPretty(data, {indent: 2, maxLength: 40});
  }

  componentDidMount () {
    this._doc = createEditor(
      this._el!,
      this.getPrettyJson(this.props.value),
      this.props.lintType || "layer",
      (value:string) => this.props.onChange(value ? JSON.parse(value) : {}),
      this.props.spec
    );
    /*CodeMirror(this._el!, {
      value: this.props.getValue!(this.props.layer),
      mode: this.props.mode || {
        name: "mgl",
      },
      lineWrapping: this.props.lineWrapping,
      tabSize: 2,
      theme: "maputnik",
      viewportMargin: Infinity,
      lineNumbers: this.props.lineNumbers,
      lint: this.props.lint || {
        context: "layer"
      },
      matchBrackets: true,
      gutters: this.props.gutters,
      scrollbarStyle: "null",
    });
    

    this._doc.on("change", this.onChange);
    this._doc.on("focus", this.onFocus);
    this._doc.on("blur", this.onBlur);
    */
    this._doc.dispatch();
  }

  onPointerDown = () => {
    this._keyEvent = "pointer";
  };

  onFocus = () => {
    if (this.props.onFocus) this.props.onFocus();
    this.setState({
      isEditing: true,
      showMessage: (this._keyEvent === "keyboard"),
    });
  };

  onBlur = () => {
    this._keyEvent = "keyboard";
    if (this.props.onBlur) this.props.onBlur();
    this.setState({
      isEditing: false,
      showMessage: false,
    });
  };

  componentWillUnMount () {
    //this._doc!.off("change", this.onChange);
    //this._doc!.off("focus", this.onFocus);
    //this._doc!.off("blur", this.onBlur);
  }

  componentDidUpdate(_prevProps: InputJsonProps) {
    //if (!this.state.isEditing && prevProps.layer !== this.props.layer) {
    //  this._cancelNextChange = true;
    //  this._doc!.setValue(
    //    this.props.getValue!(this.props.layer),
    //  );
    //}
  }

  onChange = (_e: unknown) => {
    /*
    if (this._cancelNextChange) {
      this._cancelNextChange = false;
      this.setState({
        prevValue: this._doc!.getValue(),
      });
      return;
    }
    const newCode = this._doc!.getValue();

    if (this.state.prevValue !== newCode) {
      let parsedLayer, err;
      try {
        parsedLayer = JSON.parse(newCode);
      } catch(_err) {
        err = _err;
        console.warn(_err);
      }

      if (err && this.props.onJSONInvalid) {
        this.props.onJSONInvalid();
      }
      else {
        if (this.props.onChange) this.props.onChange(parsedLayer);
        if (this.props.onJSONValid) this.props.onJSONValid();
      }
    }

    this.setState({
      prevValue: newCode,
    });
    */
  };

  render() {
    const style = {} as {maxHeight?: number};
    if (this.props.maxHeight) {
      style.maxHeight = this.props.maxHeight;
    }

    return <div className="JSONEditor" onPointerDown={this.onPointerDown} aria-hidden="true">
      <div
        className={classnames("codemirror-container", this.props.className)}
        ref={(el) => {this._el = el;}}
        style={style}
      />
    </div>;
  }
}

const InputJson = withTranslation()(InputJsonInternal);
export default InputJson;
