import * as React from 'react';
import { NextPage } from 'next';
import * as ReachSlider from '@reach/slider';
import '@reach/slider/styles.css';
import {delegateTriggerChangeEvent} from '../../../utils/event';
import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol';

interface RenderOptionsProps {
  options: (number | SelectControlBase.SelectOption)[],
  optionComponent?: React.ElementType,
  optgroupComponent?: React.ElementType,
  level?: number,
}

export enum SliderOrientation {
  HORIZONTAL = 'horizontal',
  VERTICAL = 'vertical',
}

export interface SliderProps extends Omit<React.HTMLProps<HTMLInputElement>, 'type' | 'min' | 'max' | 'list'> {
  enhanced?: boolean;
  orient?: SliderOrientation;
  min?: number;
  max?: number;
  block?: boolean;
  tickMarks?: (number | SelectControlBase.SelectOption)[];
}

const RenderOptions: React.FC<RenderOptionsProps> = ({
  options,
  optionComponent: Option = 'option',
  optgroupComponent: Optgroup = 'optgroup',
  level = 0,
}: RenderOptionsProps) => (
  <>
    {
      options.map((o) => {
        if (typeof o === 'number') {
          return (
            <Option
              key={`${o}:${o}`}
              value={o}
            />
          )
        }

        if (typeof o.value !== 'undefined') {
          return (
            <Option
              key={`${o.label}:${o.value.toString()}`}
              value={o.value}
              label={o.label}
            >
              {o.label}
            </Option>
          );
        }

        if (typeof o.children !== 'undefined') {
          if (level === 0) {
            return (
              <Optgroup
                key={o.label}
                label={o.label}
              >
                <RenderOptions
                  options={o.children}
                  optionComponent={Option}
                  optgroupComponent={Optgroup}
                  level={level + 1}
                />
              </Optgroup>
            );
          }
          return (
            <React.Fragment
              key={o.label}
            >
              <Option
                disabled
              >
                {o.label}
              </Option>
              <RenderOptions
                options={o.children}
                optionComponent={Option}
                optgroupComponent={Optgroup}
                level={level + 1}
              />
            </React.Fragment>
          );
        }

        return null;
      })
    }
  </>
);

export const Slider = React.forwardRef<HTMLInputElement, SliderProps>(({
  enhanced = false,
  orient = SliderOrientation.HORIZONTAL,
  min = 0,
  max = 100,
  step = 'any',
  block = false,
  tickMarks = [],
  onChange,
  ...etcProps
}, forwardedRef) => {
  const [renderEnhanced, setRenderEnhanced] = React.useState(false);
  const defaultRef = React.useRef<HTMLInputElement>(null);
  const ref = forwardedRef ?? defaultRef;
  const nonStandardProps: any = {
    // Gecko (Firefox) fallback
    orient,
  }
  const tickMarkId = React.useId();

  React.useEffect(() => {
    setRenderEnhanced(enhanced);
  }, [enhanced]);

  const handleEnhancedRangeChange = (newValue: number) => {
    if (typeof ref === 'object' && ref.current) {
      delegateTriggerChangeEvent(ref.current, newValue);
    }
  }

  return (
    <>
      {
        tickMarks.length > 0
        && (
          <datalist
            id={tickMarkId}
          >
            <RenderOptions
              options={tickMarks}
            />
          </datalist>
        )
      }
      <div>
        <input
          {...etcProps}
          {...nonStandardProps}
          min={min}
          max={max}
          step={step ?? 'any'}
          type="range"
          className={`${renderEnhanced ? 'sr-only' : ''} ${block ? 'w-full' : ''}`}
          ref={ref}
          list={tickMarks.length > 0 ? tickMarkId : undefined}
          onChange={onChange}
        />
        {
          renderEnhanced
          && (
            <>
              <ReachSlider.SliderInput
                min={min}
                max={max}
                step={typeof step === 'number' ? step : Number.EPSILON}
                orientation={orient === SliderOrientation.VERTICAL ? ReachSlider.SliderOrientation.Vertical : ReachSlider.SliderOrientation.Horizontal}
                onChange={handleEnhancedRangeChange}
              >
                <ReachSlider.SliderTrack>
                  <ReachSlider.SliderRange />
                    <RenderOptions options={tickMarks} optionComponent={ReachSlider.SliderMarker} optgroupComponent={React.Fragment} />
                  <ReachSlider.SliderHandle />
                </ReachSlider.SliderTrack>
              </ReachSlider.SliderInput>
            </>
          )
        }
      </div>
    </>
  );
});

Slider.displayName = 'Slider';

const NumberPage: NextPage = () => {
  return (
    <main className="my-16 md:my-32">
      <section>
        <div className="container mx-auto px-4">
          <h1>
            Number
          </h1>
          <div>
            <section>
              <h2>
                Spinner
              </h2>
              <div>
                TODO

                <input type="number" />
              </div>
            </section>
            <section>
              <h2>
                Slider
              </h2>
              <div>
                <Slider
                  min={-100}
                  max={100}
                  tickMarks={[{ label: 'low', value: 25, }, 50]}
                  enhanced
                />
              </div>
            </section>
            <section>
              <h2>
                Matrix
              </h2>
              <div>
                TODO
                <input type="range" />
                <input type="range" />
              </div>
            </section>
          </div>
        </div>
      </section>
    </main>
  )
}

export default NumberPage;
