import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import React, {
  forwardRef,
  useEffect,
  useImperativeHandle,
  useRef,
} from 'react';
import RelationSvg from '../MonacoDiffEditorRelation/RelationSvg';
import { RelationTypeEnum } from '../types';

import createEditor from './createEditor';
import './index.scss';

export interface UpdateRelationOption {
  label: string;
  value: string;
  content: string;
  range?: [number, number];
}

export interface UpdateRelationProps {
  fromOptionValue: string;
  fromRange: [number, number];
  toOptionValue: string;
  toRange: [number, number];
  fromOptions: UpdateRelationOption[];
  toOptions: UpdateRelationOption[];
  onFromOptionValueChange?: (rev: string) => void;
  onFromRangeChange?: (range: [number, number]) => void;
  onToOptionValueChange?: (rev: string) => void;
  onToRangeChange?: (editor: [number, number]) => void;
}

export interface UpdateRelationRef {
  layout: () => void;
}

const UpdateRelation = forwardRef<UpdateRelationRef, UpdateRelationProps>(
  (
    {
      fromOptionValue,
      fromRange,
      toOptionValue,
      toRange,
      fromOptions,
      toOptions,
      onFromOptionValueChange,
      onFromRangeChange,
      onToOptionValueChange,
      onToRangeChange,
    },
    ref
  ) => {
    const fromEditorElRef = useRef<HTMLDivElement>(null);
    const toEditorElRef = useRef<HTMLDivElement>(null);
    const relationSvgElRef = useRef<SVGSVGElement>(null);
    const fromEditorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(
      null
    );
    const toEditorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(
      null
    );
    const relationSvgRef = useRef<RelationSvg | null>(null);

    useImperativeHandle(
      ref,
      () => ({
        layout: () => {
          fromEditorRef.current?.layout();
          toEditorRef.current?.layout();
        },
      }),
      []
    );

    useEffect(() => {
      fromEditorRef.current = createEditor(fromEditorElRef.current);
      toEditorRef.current = createEditor(toEditorElRef.current);
    }, []);

    useEffect(() => {
      fromEditorRef.current?.setValue(
        fromOptions.find(o => o.value === fromOptionValue)?.content || ''
      );
    }, [fromOptionValue]);

    useEffect(() => {
      toEditorRef.current?.setValue(
        toOptions.find(o => o.value === toOptionValue)?.content || ''
      );
    }, [toOptionValue]);

    useEffect(() => {
      if (!relationSvgElRef.current) return;

      relationSvgRef.current = new RelationSvg(
        {
          getOriginalEditor() {
            return fromEditorRef.current;
          },
          getModifiedEditor() {
            return fromEditorRef.current;
          },
        } as any,
        {
          getOriginalEditor() {
            return toEditorRef.current;
          },
          getModifiedEditor() {
            return toEditorRef.current;
          },
        } as any,
        [],
        relationSvgElRef.current,
        {
          fromContainerDomNode: fromEditorRef.current!.getContainerDomNode(),
          toContainerDomNode: toEditorRef.current!.getContainerDomNode(),
        }
      );
    }, []);

    useEffect(() => {
      relationSvgRef.current?.setRelations([
        {
          fromRange: fromRange,
          toRange: toRange,
          type: RelationTypeEnum.relate,
        },
      ]);
      relationSvgRef.current?.renderLinks();
    }, [fromRange, toRange]);

    return (
      <div className="UpdateRelation">
        <div className="UpdateRelation__FormContainer">
          <div className="UpdateRelation__Form">
            <div className="UpdateRelation__Form__Item">
              Content:
              <ul className="UpdateRelation__Options">
                {fromOptions.map(option => {
                  return (
                    <li key={option.value}>
                      <label>
                        <input
                          type="radio"
                          onChange={() => {
                            onFromOptionValueChange?.(option.value);
                          }}
                          name="fromOptionValue"
                          value={option.value}
                          checked={fromOptionValue === option.value}
                        />
                        {option.label}
                      </label>
                    </li>
                  );
                })}
              </ul>
            </div>
            <div className="UpdateRelation__Form__Item">
              Range: L
              <input
                type="number"
                onChange={e => {
                  onFromRangeChange?.([Number(e.target.value), fromRange?.[1]]);
                }}
                value={fromRange?.[0]}
              />
              -L
              <input
                type="number"
                onChange={e => {
                  onFromRangeChange?.([fromRange?.[0], Number(e.target.value)]);
                }}
                value={fromRange?.[1]}
              />
            </div>
          </div>
          <div className="UpdateRelation__Form">
            <div className="UpdateRelation__Form__Item">
              Content:
              <ul className="UpdateRelation__Options">
                {toOptions.map(option => {
                  return (
                    <li key={option.value}>
                      <label>
                        <input
                          type="radio"
                          onChange={() => {
                            onToOptionValueChange?.(option.value);
                          }}
                          name="toOptionValue"
                          value={option.value}
                          checked={toOptionValue === option.value}
                        />
                        {option.label}
                      </label>
                    </li>
                  );
                })}
              </ul>
            </div>
            <div className="UpdateRelation__Form__Item">
              Range: L
              <input
                type="number"
                onChange={e => {
                  onToRangeChange?.([Number(e.target.value), toRange?.[1]]);
                }}
                value={toRange?.[0]}
              />
              -L
              <input
                type="number"
                onChange={e => {
                  onToRangeChange?.([toRange?.[0], Number(e.target.value)]);
                }}
                value={toRange?.[1]}
              />
            </div>
          </div>
        </div>
        <div className="UpdateRelation__RelationContainer">
          <div className="UpdateRelation__EditorContainer">
            <div className="UpdateRelation__Editor" ref={fromEditorElRef}></div>
            <div className="UpdateRelation__Editor" ref={toEditorElRef}></div>
          </div>
          <svg
            className="UpdateRelation__RelationSvg"
            ref={relationSvgElRef}
          ></svg>
        </div>
      </div>
    );
  }
);

export default UpdateRelation;
