/* eslint jsx-a11y/click-events-have-key-events: warn */

/* eslint jsx-a11y/no-static-element-interactions: warn */
import React, {
  ReactNode,
  useCallback,
  useEffect,
  useRef,
  useState,
} from 'react';
import { twMerge } from 'tailwind-merge';
import { useIsClient } from 'usehooks-ts';

export type RenderContentProps = {
  onToggle: () => void;
  onExpand?: () => void;
  onCollapse?: () => void;
};

export interface CollapsibleBannerProps {
  /** Additional className for the root container */
  className?: string;
  /** Content to show when collapsed. Can be a ReactNode or render function */
  collapsedContent: ReactNode | ((props: RenderContentProps) => ReactNode);
  /** Content to show when expanded. Can be a ReactNode or render function */
  expandedContent: ReactNode | ((props: RenderContentProps) => ReactNode);
  /** Content that persists across both collapsed and expanded states. Can be a ReactNode or render function */
  persistentContent?:
    | ReactNode
    | ((props: RenderContentProps & { isCollapsed: boolean }) => ReactNode);
  /** localStorage key to persist collapse state */
  localStorageKey?: string;
  /** Initial collapsed state */
  defaultCollapsed?: boolean;
  /** Callback when collapse state changes */
  onCollapse?: (collapsed: boolean) => void;
  /** Controlled collapsed state - when provided, component becomes controlled */
  collapsed?: boolean;
  /** Transition duration in milliseconds */
  transitionDuration?: number;
  /** Additional inline styles */
  style?: React.CSSProperties;
  /** Whether clicking anywhere on collapsed content expands it (default: true) */
  collapsedClickable?: boolean;
  /** Additional className for collapsed content container */
  collapsedClassName?: string;
  /** Additional className for expanded content container */
  expandedClassName?: string;
}

export type Props = Omit<
  React.HTMLAttributes<HTMLDivElement>,
  keyof CollapsibleBannerProps
> &
  CollapsibleBannerProps;

/**
 * CollapsibleBanner - A component that smoothly transitions between collapsed and expanded states
 *
 * When collapsedClickable is true (default), clicking anywhere on the collapsed content will expand it.
 * To prevent specific elements from triggering expansion, use event.stopPropagation() on their click handlers.
 *
 * @example
 * <CollapsibleBanner
 *   localStorageKey="myBanner"
 *   collapsedContent={({ onExpand }) => (
 *     <div>
 *       <h2>Click anywhere to expand</h2>
 *       <button onClick={(e) => { e.stopPropagation(); console.log('Button clicked'); }}>
 *         This button won't expand
 *       </button>
 *     </div>
 *   )}
 *   expandedContent={({ onCollapse }) => (
 *     <div>
 *       <h2>Expanded content</h2>
 *       <button onClick={onCollapse}>Collapse</button>
 *     </div>
 *   )}
 * />
 */
const CollapsibleBanner: React.FC<Props> = ({
  className,
  collapsedContent,
  expandedContent,
  persistentContent,
  localStorageKey,
  defaultCollapsed = false,
  onCollapse,
  collapsed: controlledIsCollapsed,
  transitionDuration = 500,
  style,
  collapsedClickable = true,
  collapsedClassName,
  expandedClassName,
  ...restProps
}) => {
  // Determine if component is controlled
  const isControlled = controlledIsCollapsed !== undefined;

  const [internalIsCollapsed, setInternalIsCollapsed] =
    useState(defaultCollapsed);
  const isClient = useIsClient();
  const [expandedHeight, setExpandedHeight] = useState<number | 'auto'>('auto');
  const [collapsedHeight, setCollapsedHeight] = useState<number | 'auto'>(
    'auto'
  );

  // Use controlled state if available, otherwise use internal state
  const isCollapsed = controlledIsCollapsed ?? internalIsCollapsed;

  const expandedContentRef = useRef<HTMLDivElement>(null);
  const collapsedContentRef = useRef<HTMLDivElement>(null);
  const persistentContentRef = useRef<HTMLDivElement>(null);

  // Load collapsed state from localStorage on mount (only for uncontrolled mode)
  useEffect(() => {
    if (!isControlled && localStorageKey && isClient) {
      const savedState = localStorage.getItem(localStorageKey);
      if (savedState !== null) {
        setInternalIsCollapsed(savedState === 'true');
      }
    }
  }, [localStorageKey, isClient, isControlled]);

  // Measure content heights
  useEffect(() => {
    if (!isClient) return;

    const measureHeights = () => {
      let persistentHeight = 0;

      // Measure persistent content height
      if (persistentContentRef.current) {
        persistentHeight = persistentContentRef.current.scrollHeight;
      }

      // Temporarily make elements position relative for accurate measurement
      if (expandedContentRef.current) {
        const expandedEl = expandedContentRef.current;
        const originalPosition = expandedEl.style.position;
        const originalOpacity = expandedEl.style.opacity;
        expandedEl.style.position = 'relative';
        expandedEl.style.opacity = '1';
        setExpandedHeight(expandedEl.scrollHeight + persistentHeight);
        expandedEl.style.position = originalPosition;
        expandedEl.style.opacity = originalOpacity;
      }
      if (collapsedContentRef.current) {
        const collapsedEl = collapsedContentRef.current;
        const originalPosition = collapsedEl.style.position;
        const originalOpacity = collapsedEl.style.opacity;
        collapsedEl.style.position = 'relative';
        collapsedEl.style.opacity = '1';
        setCollapsedHeight(collapsedEl.scrollHeight + persistentHeight);
        collapsedEl.style.position = originalPosition;
        collapsedEl.style.opacity = originalOpacity;
      }
    };

    const measureTimeout = setTimeout(measureHeights, 10);

    // Create ResizeObserver to monitor bounding box changes
    const resizeObserver = new ResizeObserver(() => {
      measureHeights();
    });

    // Observe the relevant elements for size changes
    if (persistentContentRef.current) {
      resizeObserver.observe(persistentContentRef.current);
    }
    if (expandedContentRef.current) {
      resizeObserver.observe(expandedContentRef.current);
    }
    if (collapsedContentRef.current) {
      resizeObserver.observe(collapsedContentRef.current);
    }

    return () => {
      clearTimeout(measureTimeout);
      resizeObserver.disconnect();
    };
  }, [isClient]);

  // Handle collapse/expand
  const handleToggleCollapse = useCallback(
    (collapsed?: boolean) => {
      if (isControlled) {
        // In controlled mode, call the callback
        const newState =
          collapsed !== undefined ? collapsed : !controlledIsCollapsed;
        onCollapse?.(newState);
        if (localStorageKey) {
          localStorage.setItem(localStorageKey, String(newState));
        }
      } else {
        // In uncontrolled mode, update internal state
        setInternalIsCollapsed((prev) => {
          let newState;
          if (collapsed !== undefined) {
            newState = collapsed;
          } else {
            newState = !prev;
          }
          if (localStorageKey) {
            localStorage.setItem(localStorageKey, String(newState));
          }
          onCollapse?.(newState);
          return newState;
        });
      }
    },
    [isControlled, controlledIsCollapsed, localStorageKey, onCollapse]
  );

  const handleExpand = useCallback(
    () => handleToggleCollapse(false),
    [handleToggleCollapse]
  );
  const handleCollapse = useCallback(
    () => handleToggleCollapse(true),
    [handleToggleCollapse]
  );

  if (!isClient) return null;

  const renderProps: RenderContentProps = {
    onToggle: () => handleToggleCollapse(),
    onExpand: handleExpand,
    onCollapse: handleCollapse,
  };

  const persistentRenderProps = {
    ...renderProps,
    isCollapsed,
  };

  return (
    <div
      className={twMerge(
        'relative overflow-hidden transition-all ease-in-out',
        className
      )}
      style={{
        height: isCollapsed ? collapsedHeight : expandedHeight,
        transitionDuration: `${transitionDuration}ms`,
        ...style,
      }}
      {...restProps}
    >
      <div className='relative flex h-full w-full flex-col'>
        {/* Persistent content */}
        {persistentContent && (
          <div ref={persistentContentRef} className='shrink-0'>
            {typeof persistentContent === 'function'
              ? persistentContent(persistentRenderProps)
              : persistentContent}
          </div>
        )}

        <div className='relative flex-1 overflow-hidden'>
          {/* Collapsed content */}
          <div
            className={twMerge(
              'absolute inset-0 transition-opacity duration-300',
              isCollapsed
                ? 'opacity-100 delay-300'
                : 'pointer-events-none opacity-0',
              isCollapsed && collapsedClickable && 'cursor-pointer',
              isCollapsed && collapsedClassName
            )}
            onClick={
              isCollapsed && collapsedClickable ? handleExpand : undefined
            }
          >
            <div ref={collapsedContentRef}>
              {typeof collapsedContent === 'function'
                ? collapsedContent(renderProps)
                : collapsedContent}
            </div>
          </div>

          {/* Expanded content */}
          <div
            className={twMerge(
              'absolute inset-0 transition-opacity duration-300',
              !isCollapsed
                ? 'opacity-100 delay-300'
                : 'pointer-events-none opacity-0',
              !isCollapsed && expandedClassName
            )}
          >
            <div ref={expandedContentRef}>
              {typeof expandedContent === 'function'
                ? expandedContent(renderProps)
                : expandedContent}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default CollapsibleBanner;
