import { css } from '@emotion/css';
import { type SyntheticEvent, useEffect, useRef, useState } from 'react';
import Draggable, { type DraggableEventHandler } from 'react-draggable';
import { Resizable, type ResizeCallbackData } from 'react-resizable';

import { type Dimensions2D, type GrafanaTheme2, store } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { IconButton, Portal, useStyles2 } from '@grafana/ui';
import { type Scene } from 'app/features/canvas/runtime/scene';

import { InlineEditBody } from './InlineEditBody';

type Props = {
  onClose?: () => void;
  id: number;
  scene: Scene;
};

const OFFSET_X = 10;
const OFFSET_Y = 32;

export function InlineEdit({ onClose, id, scene }: Props) {
  const root = scene.root.div?.getBoundingClientRect();
  const windowHeight = window.innerHeight;
  const windowWidth = window.innerWidth;
  const ref = useRef<HTMLDivElement>(null);
  const styles = useStyles2(getStyles);
  const inlineEditKey = 'inlineEditPanel' + id.toString();

  const defaultMeasurements = { width: 400, height: 400 };
  const widthOffset = root?.width ?? defaultMeasurements.width + OFFSET_X * 2;
  const defaultX = root?.x ?? 0 + widthOffset - defaultMeasurements.width - OFFSET_X;
  const defaultY = root?.y ?? 0 + OFFSET_Y;

  const savedPlacement = store.getObject(inlineEditKey, {
    x: defaultX,
    y: defaultY,
    w: defaultMeasurements.width,
    h: defaultMeasurements.height,
  });
  const [measurements, setMeasurements] = useState<Dimensions2D>({ width: savedPlacement.w, height: savedPlacement.h });
  const [placement, setPlacement] = useState({ x: savedPlacement.x, y: savedPlacement.y });

  // Checks that placement is within browser window
  useEffect(() => {
    const minX = windowWidth - measurements.width - OFFSET_X;
    const minY = windowHeight - measurements.height - OFFSET_Y;
    if (minX < placement.x && minX > 0) {
      setPlacement({ ...placement, x: minX });
    }
    if (minY < placement.y && minY > 0) {
      setPlacement({ ...placement, y: minY });
    }
  }, [windowHeight, windowWidth, placement, measurements]);

  const onDragStop: DraggableEventHandler = (event, dragElement) => {
    let x = dragElement.x < 0 ? 0 : dragElement.x;
    let y = dragElement.y < 0 ? 0 : dragElement.y;

    setPlacement({ x: x, y: y });
    saveToStore(x, y, measurements.width, measurements.height);
  };

  const onResizeStop = (event: SyntheticEvent<Element, Event>, data: ResizeCallbackData) => {
    const { size } = data;
    setMeasurements({ width: size.width, height: size.height });
    saveToStore(placement.x, placement.y, size.width, size.height);
  };

  const saveToStore = (x: number, y: number, width: number, height: number) => {
    store.setObject(inlineEditKey, { x: x, y: y, w: width, h: height });
  };

  return (
    <Portal>
      <div className={styles.draggableWrapper}>
        <Draggable handle="strong" onStop={onDragStop} position={{ x: placement.x, y: placement.y }}>
          <Resizable height={measurements.height} width={measurements.width} onResize={onResizeStop}>
            <div
              className={styles.inlineEditorContainer}
              style={{ height: `${measurements.height}px`, width: `${measurements.width}px` }}
              ref={ref}
            >
              <strong className={styles.inlineEditorHeader}>
                <div className={styles.placeholder} />
                <div>
                  <Trans i18nKey="canvas.inline-edit.canvas-inline-editor">Canvas Inline Editor</Trans>
                </div>
                <IconButton
                  name="times"
                  size="xl"
                  className={styles.inlineEditorClose}
                  onClick={onClose}
                  tooltip={t('canvas.inline-edit.tooltip-close-inline-editor', 'Close inline editor')}
                />
              </strong>
              <div className={styles.inlineEditorContentWrapper}>
                <div className={styles.inlineEditorContent}>
                  <InlineEditBody />
                </div>
              </div>
            </div>
          </Resizable>
        </Draggable>
      </div>
    </Portal>
  );
}

const getStyles = (theme: GrafanaTheme2) => ({
  inlineEditorContainer: css({
    display: 'flex',
    flexDirection: 'column',
    background: theme.components.panel.background,
    border: `1px solid ${theme.colors.border.weak}`,
    boxShadow: theme.shadows.z3,
    zIndex: 1000,
    opacity: 1,
    minWidth: '400px',
  }),
  draggableWrapper: css({
    width: 0,
    height: 0,
  }),
  inlineEditorHeader: css({
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    background: theme.colors.background.canvas,
    borderBottom: `1px solid ${theme.colors.border.weak}`,
    height: '40px',
    cursor: 'move',
  }),
  inlineEditorContent: css({
    whiteSpace: 'pre-wrap',
    padding: '10px',
  }),
  inlineEditorClose: css({
    marginLeft: 'auto',
  }),
  placeholder: css({
    width: '24px',
    height: '24px',
    visibility: 'hidden',
    marginRight: 'auto',
  }),
  inlineEditorContentWrapper: css({
    overflow: 'scroll',
  }),
});
