//
// Copyright 2026 DXOS.org
//

import { createContext } from '@radix-ui/react-context';
import { Primitive } from '@radix-ui/react-primitive';
import { Slot } from '@radix-ui/react-slot';
import React, { CSSProperties, useMemo } from 'react';

import { type AllowedAxis, type SlottableProps } from '@dxos/ui-types';

import { useThemeContext } from '../../hooks';
import { composableProps, slottable } from '../../util';
import { scrollbar } from './scrollbar';

//
// Context
//

const SCROLLAREA_NAME = 'ScrollArea';

type ScrollAreaContextType = {
  /** Orientation of scrollbars. */
  orientation: AllowedAxis;
  /** Hide scrollbars when not scrolling. */
  autoHide: boolean;
  /** Show scrollbars. */
  scrollbars?: boolean;
  /** Apply padding to opposite side of scrollbar. */
  centered?: boolean;
  /** Apply padding. */
  padding: boolean;
  /** Use thin scrollbars. */
  thin: boolean;
  /** Enable snap scrolling. */
  snap: boolean;
};

const [ScrollAreaProvider, useScrollAreaContext] = createContext<ScrollAreaContextType>(SCROLLAREA_NAME);

//
// Root
//

const SCROLLAREA_ROOT_NAME = 'ScrollArea.Root';

type ScrollAreaRootProps = Partial<ScrollAreaContextType>;

/**
 * ScrollArea provides native scrollbars with custom styling.
 */
const ScrollAreaRoot = slottable<HTMLDivElement, ScrollAreaRootProps>(
  (
    {
      children,
      asChild,
      orientation = 'vertical',
      autoHide = true,
      scrollbars = true,
      centered = false,
      padding = false,
      thin = false,
      snap = false,
      ...props
    },
    forwardedRef,
  ) => {
    const { tx } = useThemeContext();
    const { className, ...rest } = composableProps(props);
    const Comp = asChild ? Slot : Primitive.div;
    const options = useMemo(
      () => ({ orientation, autoHide, scrollbars, centered, padding, thin, snap }),
      [orientation, autoHide, scrollbars, centered, padding, thin, snap],
    );

    return (
      <ScrollAreaProvider {...options}>
        <Comp {...rest} className={tx('scrollArea.root', options, className)} ref={forwardedRef}>
          {children}
        </Comp>
      </ScrollAreaProvider>
    );
  },
);

ScrollAreaRoot.displayName = SCROLLAREA_ROOT_NAME;

//
// Viewport
//

const SCROLLAREA_VIEWPORT_NAME = 'ScrollArea.Viewport';

type ScrollAreaViewportProps = SlottableProps;

const ScrollAreaViewport = slottable<HTMLDivElement>(({ children, asChild, ...props }, forwardedRef) => {
  const { tx } = useThemeContext();
  const options = useScrollAreaContext(SCROLLAREA_VIEWPORT_NAME);
  const density = options.thin ? scrollbar.md : scrollbar.lg;
  const { className, ...rest } = composableProps(props);
  const { style, ...restWithoutStyle } = rest as { style?: CSSProperties; [key: string]: any };
  const Comp = asChild ? Slot : Primitive.div;

  return (
    <Comp
      {...restWithoutStyle}
      style={
        {
          '--scroll-width': options.scrollbars ? `${density.size}px` : '0px',
          '--scroll-padding': options.scrollbars ? `${density.padding}px` : '0px',
          ...style,
        } as CSSProperties
      }
      className={tx('scrollArea.viewport', options, className)}
      ref={forwardedRef}
    >
      {children}
    </Comp>
  );
});

ScrollAreaViewport.displayName = SCROLLAREA_VIEWPORT_NAME;

//
// ScrollArea
//

export const ScrollArea = {
  Root: ScrollAreaRoot,
  Viewport: ScrollAreaViewport,
};

export type { ScrollAreaRootProps, ScrollAreaViewportProps };
