/**
 * SplitContainer - Smooth and functional resizable split panel
 *
 * Features:
 * - Smooth dragging with proper event handling
 * - Percentage-based sizing that works correctly
 * - Proper constraints and boundaries
 * - Clean visual feedback
 * - Touch support for mobile devices
 * - Memory of split position
 */

import React, { useState, useRef, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';

import CapRow from '@capillarytech/cap-ui-library/CapRow';

// Styles
import './_splitContainer.scss';

// Constants
const SPLIT_CONSTRAINTS = {
  MIN_PERCENT: 10,
  MAX_PERCENT: 90,
  DEFAULT_MIN_SIZE: 200
};

const DIRECTIONS = {
  HORIZONTAL: 'horizontal',
  VERTICAL: 'vertical'
};

const SplitContainer = ({
  children,
  className = '',
  defaultSplit = 50,
  minSize = SPLIT_CONSTRAINTS.DEFAULT_MIN_SIZE,
  maxSize = null,
  direction = DIRECTIONS.HORIZONTAL,
  onSplitChange = null
}) => {
  const [splitSize, setSplitSize] = useState(defaultSplit);
  const [isDragging, setIsDragging] = useState(false);

  // Refs for drag handling
  const containerRef = useRef(null);
  const startPositionRef = useRef(0);
  const startSplitRef = useRef(0);

  const handleMouseDown = useCallback((e) => {
    e.preventDefault();
    e.stopPropagation();

    setIsDragging(true);
    startPositionRef.current = direction === DIRECTIONS.HORIZONTAL ? e.clientX : e.clientY;
    startSplitRef.current = splitSize;

    // Add global styles for dragging
    const { body } = document;
    body.style.cursor = direction === DIRECTIONS.HORIZONTAL ? 'col-resize' : 'row-resize';
    body.style.userSelect = 'none';
    body.style.pointerEvents = 'none';

    // Prevent text selection during drag
    document.onselectstart = () => false;

    // Re-enable pointer events on the container
    if (containerRef.current) {
      containerRef.current.style.pointerEvents = 'auto';
    }
  }, [direction, splitSize]);

  const handleMouseMove = useCallback((e) => {
    if (!isDragging || !containerRef.current) return;

    e.preventDefault();
    e.stopPropagation();

    const container = containerRef.current;
    const containerRect = container.getBoundingClientRect();
    const isHorizontal = direction === DIRECTIONS.HORIZONTAL;
    const containerSize = isHorizontal ? containerRect.width : containerRect.height;

    if (containerSize === 0) return; // Avoid division by zero

    const currentPosition = isHorizontal ? e.clientX : e.clientY;
    const containerStart = isHorizontal ? containerRect.left : containerRect.top;
    const relativePosition = currentPosition - containerStart;

    // Calculate new split percentage
    let newSplit = (relativePosition / containerSize) * 100;

    // Apply constraints
    const minPercent = (minSize / containerSize) * 100;
    const maxPercent = maxSize
      ? ((containerSize - maxSize) / containerSize) * 100
      : SPLIT_CONSTRAINTS.MAX_PERCENT;

    // Clamp the values
    const { MIN_PERCENT, MAX_PERCENT } = SPLIT_CONSTRAINTS;
    newSplit = Math.max(MIN_PERCENT, Math.min(MAX_PERCENT, newSplit)); // Overall limits
    newSplit = Math.max(minPercent, Math.min(100 - minPercent, newSplit)); // Min size limits

    if (maxSize) {
      newSplit = Math.min(maxPercent, newSplit);
    }

    setSplitSize(newSplit);
    onSplitChange?.(newSplit); // Optional chaining
  }, [isDragging, direction, minSize, maxSize, onSplitChange]);

  const handleMouseUp = useCallback(() => {
    if (!isDragging) return;

    setIsDragging(false);

    // Reset global styles
    const { body } = document;
    body.style.cursor = '';
    body.style.userSelect = '';
    body.style.pointerEvents = '';

    // Reset text selection prevention
    document.onselectstart = null;

    containerRef.current?.style && (containerRef.current.style.pointerEvents = '');
  }, [isDragging]);

  // Touch support - convert touch events to mouse events
  const handleTouchStart = useCallback((e) => {
    const touch = e.touches?.[0];
    if (!touch) return;

    const mouseEvent = new MouseEvent('mousedown', {
      clientX: touch.clientX,
      clientY: touch.clientY
    });
    handleMouseDown(mouseEvent);
  }, [handleMouseDown]);

  const handleTouchMove = useCallback((e) => {
    if (!isDragging) return;
    e.preventDefault();

    const touch = e.touches?.[0];
    if (!touch) return;

    const mouseEvent = new MouseEvent('mousemove', {
      clientX: touch.clientX,
      clientY: touch.clientY
    });
    handleMouseMove(mouseEvent);
  }, [isDragging, handleMouseMove]);

  const handleTouchEnd = useCallback(() => {
    handleMouseUp();
  }, [handleMouseUp]);

  // Event listeners
  useEffect(() => {
    if (isDragging) {
      document.addEventListener('mousemove', handleMouseMove, { passive: false });
      document.addEventListener('mouseup', handleMouseUp);
      document.addEventListener('touchmove', handleTouchMove, { passive: false });
      document.addEventListener('touchend', handleTouchEnd);

      return () => {
        document.removeEventListener('mousemove', handleMouseMove);
        document.removeEventListener('mouseup', handleMouseUp);
        document.removeEventListener('touchmove', handleTouchMove);
        document.removeEventListener('touchend', handleTouchEnd);
      };
    }
  }, [isDragging, handleMouseMove, handleMouseUp, handleTouchMove, handleTouchEnd]);

  // Prevent text selection during drag
  useEffect(() => {
    if (isDragging) {
      // Named handlers to prevent text selection and dragging during resize
      const selectStartHandler = (e) => {
        e.preventDefault();
        return false;
      };

      const dragStartHandler = (e) => {
        e.preventDefault();
        return false;
      };

      document.addEventListener('selectstart', selectStartHandler);
      document.addEventListener('dragstart', dragStartHandler);

      return () => {
        document.removeEventListener('selectstart', selectStartHandler);
        document.removeEventListener('dragstart', dragStartHandler);
      };
    }
  }, [isDragging]);

  // Cleanup effect to reset global styles on unmount
  useEffect(() => {
    return () => {
      // Reset global body styles if component unmounts while dragging
      if (isDragging) {
        const { body } = document;
        body.style.cursor = '';
        body.style.userSelect = '';
        body.style.pointerEvents = '';

        // Reset container pointer events if ref is still available
        if (containerRef.current) {
          containerRef.current.style.pointerEvents = '';
        }
      }
    };
  }, [isDragging]);

  // Generate CSS classes
  const containerClasses = [
    'split-container',
    `split-container--${direction}`,
    isDragging ? 'split-container--dragging' : '',
    className
  ].filter(Boolean).join(' ');

  // Generate CSS custom properties for dynamic sizing
  const containerStyle = {
    '--split-size': `${splitSize}%`,
    '--remaining-size': `${100 - splitSize}%`
  };

  return (
    <div
      ref={containerRef}
      className={containerClasses}
      style={containerStyle}
    >
      <CapRow className="split-container__panel split-container__panel--first">
        {children?.[0]}
      </CapRow>

      <div
        className="split-container__splitter"
        onMouseDown={handleMouseDown}
        onTouchStart={handleTouchStart}
        role="separator"
        tabIndex={0}
        aria-label={`Resize ${direction === DIRECTIONS.HORIZONTAL ? 'horizontal' : 'vertical'} split`}
        aria-orientation={direction === DIRECTIONS.HORIZONTAL ? 'vertical' : 'horizontal'}
        aria-valuenow={Math.round(splitSize)}
        aria-valuemin={SPLIT_CONSTRAINTS.MIN_PERCENT}
        aria-valuemax={SPLIT_CONSTRAINTS.MAX_PERCENT}
      >
        <div className="split-container__splitter-handle">
          <div className="split-container__splitter-line" />
        </div>
      </div>

      <CapRow className="split-container__panel split-container__panel--second">
        {children?.[1]}
      </CapRow>
    </div>
  );
};

SplitContainer.propTypes = {
  children: PropTypes.arrayOf(PropTypes.node).isRequired,
  className: PropTypes.string,
  defaultSplit: PropTypes.number,
  minSize: PropTypes.number,
  maxSize: PropTypes.number,
  direction: PropTypes.oneOf(Object.values(DIRECTIONS)),
  onSplitChange: PropTypes.func
};

export default SplitContainer;
