import * as React from 'react';
import * as classNames from 'classnames';
import {ChangeEvent, Component, KeyboardEvent} from 'react';
import {Button} from './../';

export interface JsonDebuggerProps {
  object: any;
  spaces?: number;
  onChange?: (object: any) => void;
}

export interface State {
  editionModeEnabled: boolean;
  hasError: boolean;
  json: any;
}

export class JsonDebugger extends Component<JsonDebuggerProps, State> {
  static defaultProps: Partial<JsonDebuggerProps> = {
    spaces: 2,
    onChange: () => { return; },
  };

  state: State;
  private textarea: HTMLTextAreaElement;
  private updatedFromInside: boolean;

  constructor(props: JsonDebuggerProps) {
    super(props);

    this.state = {
      editionModeEnabled: false,
      hasError: false,
      json: this.stringifyObject(),
    };
  }

  componentDidMount() {
    this.updateTextareaHeight();
  }

  componentWillReceiveProps(nextProps: JsonDebuggerProps) {
    if (!this.updatedFromInside) {
      const json = this.stringifyObject(nextProps.object);
      this.setState({json});
    }
  }

  componentDidUpdate() {
    this.updatedFromInside = false;
    this.updateTextareaHeight();
  }

  render() {
    const {editionModeEnabled, hasError} = this.state;
    const value = this.getTextareaValue();

    return (
      <div
        className={classNames(
          'json-debugger',
          {
            active: editionModeEnabled,
            error: hasError,
          },
        )}
      >
        {hasError && (
          <div className="json-debugger-error">
            Invalid JSON
          </div>
        )}

        <textarea
          autoCorrect="off"
          autoCapitalize="off"
          ref={instance => this.textarea = instance}
          spellCheck={false}
          value={value}
          onBlur={this.setFocus(false)}
          onChange={this.handleChange}
          onFocus={this.setFocus(true)}
        />
      </div>
    );
  }

  getTextareaValue() {
    return this.updatedFromInside
      ? this.state.json
      : this.stringifyObject();
  }

  stringifyObject = (object: any = this.props.object) => {
    const {spaces} = this.props;
    return JSON.stringify(object, null, spaces);
  }

  handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
    const json = e.target.value;

    this.setState({
      hasError: false,
      json,
    });

    this.updatedFromInside = true;

    try {
      const object = JSON.parse(json);
      this.props.onChange(Object.assign({}, object));
    } catch (error) {
      this.setState({hasError: true});
    }
  }

  setFocus = (focused: boolean) => () => {
    this.setState({editionModeEnabled: focused});
  }

  updateTextareaHeight() {
    this.textarea.style.height = `${this.textarea.scrollHeight}px`;
  }
}
