import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useDraggablePanel } from '../../hooks/useDraggablePanel';
import { useFloatingWidgetGridSplit } from '../../hooks/useFloatingWidgetGridSplit';
import { useFloatingWidgetResize } from '../../hooks/useFloatingWidgetResize';
import {
  defaultGridFr,
  gridSplitPositions,
  gridTemplateFr,
  minSizeForGrid,
  type FloatingWidgetInstance,
} from '../../types/floatingWidget';
import type { WidgetSlot } from '../../types/widgetDashboard';
import WidgetSlotView from '../widgetDashboard/WidgetSlotView';
import WidgetPickerModal from '../widgetDashboard/WidgetPickerModal';

interface Props {
  instance: FloatingWidgetInstance;
  focused: boolean;
  onClose: () => void;
  onFocus: () => void;
  onUpdateSlots: (slots: WidgetSlot[]) => void;
  onUpdateSize: (width: number, height: number) => void;
  onUpdateGridFr: (rowFr: number[], colFr: number[]) => void;
  /** Tauri OS 창 — 네이티브 타이틀·리사이즈 사용, 내부 플로팅 크롬 숨김 */
  nativeShell?: boolean;
  /** 위젯 선택 팝업 열림/닫힘 (네이티브 창 크기 조절용) */
  onPickerOpenChange?: (open: boolean) => void;
}

const FloatingWidgetWindow: React.FC<Props> = ({
  instance,
  focused,
  onClose,
  onFocus,
  onUpdateSlots,
  onUpdateSize,
  onUpdateGridFr,
  nativeShell = false,
  onPickerOpenChange,
}) => {
  const [pickSlotId, setPickSlotId] = useState<string | null>(null);

  const openPicker = useCallback((slotId: string) => {
    setPickSlotId(slotId);
    onPickerOpenChange?.(true);
  }, [onPickerOpenChange]);

  const closePicker = useCallback(() => {
    setPickSlotId(null);
    onPickerOpenChange?.(false);
  }, [onPickerOpenChange]);
  const bodyWrapRef = useRef<HTMLDivElement>(null);
  const bodyRef = useRef<HTMLDivElement>(null);
  const [bodySize, setBodySize] = useState({ width: 0, height: 0 });

  const resolveBodySize = useCallback(() => {
    const wrap = bodyWrapRef.current;
    const body = bodyRef.current;
    const width = wrap?.clientWidth ?? body?.clientWidth ?? bodySize.width;
    const height = wrap?.clientHeight ?? body?.clientHeight ?? bodySize.height;
    if (width > 0 && height > 0) {
      return { width, height };
    }
    if (nativeShell) {
      return {
        width: width > 0 ? width : instance.width,
        height: height > 0 ? height : instance.height,
      };
    }
    return { width, height };
  }, [bodySize.height, bodySize.width, instance.height, instance.width, nativeShell]);

  const rowFr = instance.rowFr?.length === instance.rows
    ? instance.rowFr
    : defaultGridFr(instance.rows);
  const colFr = instance.colFr?.length === instance.cols
    ? instance.colFr
    : defaultGridFr(instance.cols);

  const rowFrRef = useRef(rowFr);
  const colFrRef = useRef(colFr);
  rowFrRef.current = rowFr;
  colFrRef.current = colFr;

  const {
    panelRef,
    setPos,
    dragging,
    onHeaderPointerDown,
    headerTouchStyle,
    panelStyle,
    headerCursor,
  } = useDraggablePanel({
    centerOnMount: false,
    initialPosition: instance.position,
  });

  const minSize = useMemo(
    () => minSizeForGrid(instance.rows, instance.cols),
    [instance.rows, instance.cols],
  );

  const handleResize = useCallback((result: { width: number; height: number; x?: number }) => {
    if (result.x != null) {
      setPos(prev => ({ ...prev, x: result.x! }));
    }
    onUpdateSize(result.width, result.height);
  }, [onUpdateSize, setPos]);

  const { onResizeSePointerDown, onResizeSwPointerDown } = useFloatingWidgetResize(
    panelRef,
    { width: instance.width, height: instance.height },
    minSize,
    handleResize,
  );

  const handleColFrChange = useCallback((next: number[]) => {
    onUpdateGridFr(rowFrRef.current, next);
  }, [onUpdateGridFr]);

  const handleRowFrChange = useCallback((next: number[]) => {
    onUpdateGridFr(next, colFrRef.current);
  }, [onUpdateGridFr]);

  const notifyLayoutSync = useCallback(() => {
    window.dispatchEvent(new CustomEvent('gc-widget-layout-sync'));
  }, []);

  const { onColSplitPointerDown, onRowSplitPointerDown } = useFloatingWidgetGridSplit({
    onColFrChange: handleColFrChange,
    onRowFrChange: handleRowFrChange,
    getColFr: () => colFrRef.current,
    getRowFr: () => rowFrRef.current,
    getBodySize: resolveBodySize,
    onSplitEnd: notifyLayoutSync,
  });

  const updateSlot = useCallback((slotId: string, updater: (s: WidgetSlot) => WidgetSlot) => {
    onUpdateSlots(instance.slots.map(s => (s.id === slotId ? updater(s) : s)));
  }, [instance.slots, onUpdateSlots]);

  const applyWidgetType = useCallback((widgetType: string) => {
    if (!pickSlotId) return;
    updateSlot(pickSlotId, s => ({ ...s, widgetType, config: {} }));
    closePicker();
  }, [pickSlotId, updateSlot, closePicker]);

  const clearWidget = useCallback((slotId: string) => {
    updateSlot(slotId, s => ({ ...s, widgetType: null, config: {} }));
  }, [updateSlot]);

  /* 창·그리드 크기 변경 시 내부 차트·위젯 레이아웃 재동기화 */
  useEffect(() => {
    const el = bodyWrapRef.current ?? bodyRef.current;
    if (!el) return undefined;
    let timer: ReturnType<typeof setTimeout> | null = null;
    const scheduleLayoutSync = () => {
      const wrap = bodyWrapRef.current;
      const body = bodyRef.current;
      setBodySize({
        width: wrap?.clientWidth ?? body?.clientWidth ?? 0,
        height: wrap?.clientHeight ?? body?.clientHeight ?? 0,
      });
      if (timer) clearTimeout(timer);
      timer = setTimeout(notifyLayoutSync, 80);
    };
    scheduleLayoutSync();
    const ro = new ResizeObserver(() => scheduleLayoutSync());
    ro.observe(el);
    if (bodyRef.current && bodyRef.current !== el) {
      ro.observe(bodyRef.current);
    }
    return () => {
      if (timer) clearTimeout(timer);
      ro.disconnect();
    };
  }, [instance.rows, instance.cols, notifyLayoutSync]);

  const colSplitPositions = useMemo(
    () => gridSplitPositions(colFr, bodySize.width, 1),
    [colFr, bodySize.width],
  );
  const rowSplitPositions = useMemo(
    () => gridSplitPositions(rowFr, bodySize.height, 1),
    [rowFr, bodySize.height],
  );

  const title = `위젯 ${instance.rows}×${instance.cols}`;

  const windowClass = [
    'fw-window',
    focused ? 'fw-window--focused' : '',
    nativeShell ? 'fw-window--native' : '',
  ].filter(Boolean).join(' ');

  const windowStyle = nativeShell
    ? undefined
    : {
        ...panelStyle,
        width: instance.width,
        height: instance.height,
        zIndex: instance.zIndex,
        cursor: dragging ? 'grabbing' : undefined,
      };

  const content = (
    <>
      <div
        ref={panelRef}
        className={windowClass}
        style={windowStyle}
        onMouseDown={nativeShell ? undefined : onFocus}
      >
        {!nativeShell && (
          <div
            className="fw-window-head"
            onPointerDown={onHeaderPointerDown}
            style={{ cursor: headerCursor, ...headerTouchStyle }}
          >
            <span className="fw-window-title">{title}</span>
            <button
              type="button"
              className="fw-window-close"
              title="위젯 창 닫기"
              aria-label="위젯 창 닫기"
              onPointerDown={e => e.stopPropagation()}
              onClick={onClose}
            >
              ✕
            </button>
          </div>
        )}

        <div ref={bodyWrapRef} className="fw-window-body-wrap">
          <div
            ref={bodyRef}
            className="fw-window-body"
            style={{
              gridTemplateRows: gridTemplateFr(rowFr),
              gridTemplateColumns: gridTemplateFr(colFr),
            }}
          >
            {instance.slots.map(slot => (
              <div key={slot.id} className="fw-window-cell">
                <WidgetSlotView
                  slot={slot}
                  zoneId="center"
                  onPickWidget={openPicker}
                  onClearWidget={id => clearWidget(id)}
                />
              </div>
            ))}
          </div>

          {(colSplitPositions.length > 0 || rowSplitPositions.length > 0) && (
            <div className="fw-grid-split-layer" aria-hidden="true">
              {colSplitPositions.map((left, index) => (
                <div
                  key={`col-${index}`}
                  className="fw-grid-splitter fw-grid-splitter--v"
                  style={{ left }}
                  data-no-chart-pan="true"
                  role="separator"
                  aria-orientation="vertical"
                  aria-label={`열 ${index + 1}·${index + 2} 경계 조절`}
                  onPointerDown={onColSplitPointerDown(index)}
                />
              ))}
              {rowSplitPositions.map((top, index) => (
                <div
                  key={`row-${index}`}
                  className="fw-grid-splitter fw-grid-splitter--h"
                  style={{ top }}
                  data-no-chart-pan="true"
                  role="separator"
                  aria-orientation="horizontal"
                  aria-label={`행 ${index + 1}·${index + 2} 경계 조절`}
                  onPointerDown={onRowSplitPointerDown(index)}
                />
              ))}
            </div>
          )}
        </div>

        {!nativeShell && (
          <div className="fw-window-resize-layer">
            <div
              className="fw-window-resize-handle fw-window-resize-handle--sw"
              role="separator"
              aria-label="위젯 창 크기 조절 (좌하단)"
              onPointerDown={onResizeSwPointerDown}
            />
            <div
              className="fw-window-resize-handle fw-window-resize-handle--se"
              role="separator"
              aria-label="위젯 창 크기 조절 (우하단)"
              onPointerDown={onResizeSePointerDown}
            />
          </div>
        )}
      </div>

      <WidgetPickerModal
        open={pickSlotId != null}
        category="all"
        nativeCompact={nativeShell}
        onClose={closePicker}
        onSelect={applyWidgetType}
      />
    </>
  );

  return nativeShell ? content : createPortal(content, document.body);
};

export default FloatingWidgetWindow;
