import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import axios from 'B/axios';

import { DashConfig, DashSetting, IframeConfig } from '../types';

const NAVBAR_BOUNDARY_SELECTORS = [
  '[data-dashboard-navbar-root]',
  '[data-menubar-root]',
] as const;

const getNavbarElements = (): HTMLElement[] => {
  if (typeof document === 'undefined') {
    return [];
  }
  const elements: HTMLElement[] = [];
  NAVBAR_BOUNDARY_SELECTORS.forEach((selector) => {
    elements.push(...Array.from(document.querySelectorAll<HTMLElement>(selector)));
  });
  return elements;
};

const clampTopToNavbarBounds = (
  candidateTop: number,
  windowElement?: HTMLElement | null,
): number => {
  if (typeof document === 'undefined') {
    return candidateTop;
  }
  const offsetParent =
    (windowElement?.offsetParent as HTMLElement | null) ??
    document.body ??
    null;

  if (!offsetParent) {
    return candidateTop;
  }

  const parentRect = offsetParent.getBoundingClientRect();
  const parentTop = parentRect?.top ?? 0;

  let minTop = 0;
  NAVBAR_BOUNDARY_SELECTORS.forEach((selector) => {
    document.querySelectorAll<HTMLElement>(selector).forEach((node) => {
      const rect = node.getBoundingClientRect();
      const relativeBottom = rect.bottom - parentTop;
      if (relativeBottom > minTop) {
        minTop = relativeBottom;
      }
    });
  });

  return candidateTop < minTop ? minTop : candidateTop;
};

const readNumericStyleValue = (
  element: HTMLElement | null,
  property: 'top' | 'left',
): number => {
  if (!element) {
    return 0;
  }
  const raw = property === 'top' ? element.style.top : element.style.left;
  const parsed = parseFloat(raw ?? '');
  if (Number.isFinite(parsed)) {
    return parsed;
  }
  return property === 'top' ? element.offsetTop : element.offsetLeft;
};

// Debounce-Cache für storePosition
const pendingStores = new Map<number, ReturnType<typeof setTimeout>>();

function storePosition(dashID: number, rect) {
  // Vorherige pending requests für dieses Dashboard abbrechen
  const existing = pendingStores.get(dashID);
  if (existing) {
    clearTimeout(existing);
  }

  // Neuen debounced call setzen (100ms Verzögerung)
  const timeoutId = setTimeout(() => {
    let x = rect.left;
    let y = rect.top;
    if (x < 0) {
      x = 0;
    }
    if (y < 0) {
      y = 0;
    }

    const pos = {
      x: x,
      y: y,
      z_index: rect.z_index,
      height: rect.height,
      width: rect.width,
    };
    const payload = { dash_id: dashID, dash_pos: pos };

    axios.post('/dashboardpos/save', payload);
    pendingStores.delete(dashID);
  }, 100);

  pendingStores.set(dashID, timeoutId);
}

export function useWindow(
  greatestZIndex,
  setZIndex,
  setting: DashConfig | IframeConfig
) {
  const initPosition = setting.position;
  const [windowPosition, setWindowPosition] = useState({
    top: initPosition.y || 0,
    left: initPosition.x || 0,
    z_index: initPosition.z_index || 100,
  });
  const windowRef = useRef(null);

  let windowZIndex = 100;
  let pos1 = 0,
    pos2 = 0,
    pos3 = 0,
    pos4 = 0;

  const [isSettingDialogOpen, setIsSettingDialogOpen] = useState(false);
  const openSettingDialog = () => setIsSettingDialogOpen(true);
  const closeSettingDialog = () => setIsSettingDialogOpen(false);

  useLayoutEffect(() => {
    if (!windowRef.current) {
      return;
    }
    setWindowPosition((prev) => {
      const safeTop = clampTopToNavbarBounds(prev.top, windowRef.current);
      if (safeTop === prev.top) {
        return prev;
      }
      return { ...prev, top: safeTop };
    });
  }, [setting.id]);

  useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    const handleResize = () => {
      if (!windowRef.current) {
        return;
      }
      setWindowPosition((prev) => {
        const safeTop = clampTopToNavbarBounds(prev.top, windowRef.current);
        if (safeTop === prev.top) {
          return prev;
        }
        return { ...prev, top: safeTop };
      });
    };

    window.addEventListener('resize', handleResize);

    let observer: ResizeObserver | null = null;
    if (typeof ResizeObserver !== 'undefined') {
      const navbars = getNavbarElements();
      if (navbars.length > 0) {
        observer = new ResizeObserver(() => handleResize());
        navbars.forEach((el) => observer?.observe(el));
      }
    }

    return () => {
      window.removeEventListener('resize', handleResize);
      observer?.disconnect();
    };
  }, []);

  function onDrag(e: MouseEvent) {
    e.stopPropagation();
    e.preventDefault();

    if (!windowRef.current) {
      return;
    }

    const z_index = greatestZIndex + 1;
    setZIndex(z_index);
    windowZIndex = z_index;

    pos1 = pos3 - e.clientX;
    pos2 = pos4 - e.clientY;
    pos3 = e.clientX;
    pos4 = e.clientY;

    const top = clampTopToNavbarBounds(windowRef.current.offsetTop - pos2, windowRef.current);
    const left = windowRef.current.offsetLeft - pos1;
    setWindowPosition({ top, left, z_index });
  }

  function docEventCleanup() {
    if (!windowRef.current) {
      document.removeEventListener('mousemove', onDrag);
      return;
    }
    const rawTop = readNumericStyleValue(windowRef.current, 'top');
    const top = clampTopToNavbarBounds(rawTop, windowRef.current);
    const left = readNumericStyleValue(windowRef.current, 'left');

    storePosition(setting.id, {
      top,
      left,
      width: setting.position.width,
      height: setting.position.height,
      z_index: windowZIndex,
    });
    document.removeEventListener('mousemove', onDrag);
  }

  function onResizeStop({ width, height }) {
    if (!windowRef.current) {
      return;
    }
    const rawTop = readNumericStyleValue(windowRef.current, 'top');
    const top = clampTopToNavbarBounds(rawTop, windowRef.current);
    const left = readNumericStyleValue(windowRef.current, 'left');
    setWindowPosition((prev) => {
      if (prev.top === top) {
        return prev;
      }
      return { ...prev, top };
    });
    storePosition(setting.id, {
      top,
      left,
      width,
      height,
    });
  }

  function onMouseDown(e: MouseEvent) {
    e.stopPropagation();
    e.preventDefault();
    pos3 = e.clientX;
    pos4 = e.clientY;

    const z_index = greatestZIndex + 1;
    setZIndex(z_index);
    windowZIndex = z_index;
    setWindowPosition((prev) => ({ ...prev, z_index }));

    document.addEventListener('mouseup', docEventCleanup, { once: true });
    document.addEventListener('mousemove', onDrag);
  }

  return [
    { windowRef, windowPosition, isSettingDialogOpen },
    {
      onMouseDown,
      onResizeStop,
      openSettingDialog,
      closeSettingDialog,
    },
  ];
}

export function useContentArea(initWidth, initHeight, onResizeStop) {
  enum Resize {
    Load = 1,
    Start,
    Stop,
  }
  const contentAreaRef = useRef(null);
  const [width, setWidth] = useState(initWidth);
  const [height, setHeight] = useState(initHeight);
  const [_resize, setResize] = useState(Resize.Load);

  useEffect(() => {
    if (_resize === Resize.Stop) {
      onResizeStop({ width, height });
    }
  }, [Resize.Stop, _resize, height, onResizeStop, width]);

  let startX: number,
    startY = 0;
  let moveX: number,
    moveY = 0;

  function resize(e: MouseEvent) {
    e.stopPropagation();
    e.preventDefault();
    moveX = e.clientX;
    setWidth(width + (moveX - startX));

    moveY = e.clientY;
    setHeight(height + (moveY - startY));
  }

  function cleanupResize() {
    document.removeEventListener('mousemove', resize);
    setResize(Resize.Stop);
  }

  function onGrabAreaClick(e: MouseEvent) {
    e.stopPropagation();
    e.preventDefault();
    startX = e.clientX;
    startY = e.clientY;

    document.addEventListener('mousemove', resize);
    document.addEventListener('mouseup', () => cleanupResize(), { once: true });
    setResize(Resize.Start);
  }

  return [{ contentAreaRef, width, height }, { onGrabAreaClick }];
}
