import * as React from 'react';
import useSize from '@react-hook/size';
import { FixedSizeList as List } from 'react-window';
import { ExclamationTriangleIcon } from '@heroicons/react/24/outline';
import { ContainerProperties } from '../ContainerProperties/ContainerProperties';
import { type IContainerModel } from '../../Interfaces/IContainerModel';
import { FindContainerById, MakeRecursionDFSIterator } from '../../utils/itertools';
import { type ISymbolModel } from '../../Interfaces/ISymbolModel';
import { type PropertyType } from '../../Enums/PropertyType';
import { ToggleSideBar } from '../Sidebar/ToggleSideBar/ToggleSideBar';
import { Text } from '../Text/Text';
import { ExtendedSidebar } from '../UI/UI';

interface IElementsSidebarProps {
  containers: Map<string, IContainerModel>
  mainContainer: IContainerModel
  symbols: Map<string, ISymbolModel>
  selectedContainer: IContainerModel | undefined
  selectedExtendedSidebar: ExtendedSidebar
  onPropertyChange: (
    key: string,
    value: string | number | boolean | number[],
    type?: PropertyType
  ) => void
  selectContainer: (containerId: string) => void
  addContainer: (index: number, type: string, parent: string) => void
  onExpandChange: (value: ExtendedSidebar) => void
}

function RemoveBorderClasses(target: HTMLElement, exception: string = ''): void {
  const bordersClasses = ['border-t-8', 'border-8', 'border-b-8'].filter(className => className !== exception);
  target.classList.remove(...bordersClasses);
}

function HandleDragLeave(event: React.DragEvent): void {
  let target: HTMLButtonElement = event.target as HTMLButtonElement;

  if (target.classList.contains('list-vertical-bar')) {
    target = target.parentElement as HTMLButtonElement;
  }

  RemoveBorderClasses(target);
}

function HandleDragOver(
  event: React.DragEvent,
  mainContainer: IContainerModel
): void {
  event.preventDefault();
  let target = event.target as HTMLElement;

  if (target.classList.contains('list-vertical-bar')) {
    target = target.parentElement as HTMLButtonElement;
  }

  const rect = target.getBoundingClientRect();
  const y = event.clientY - rect.top; // y position within the element.

  if (target.id === mainContainer.properties.id) {
    target.classList.add('border-8');
    return;
  }

  if (y < 12) {
    RemoveBorderClasses(target, 'border-t-8');
    target.classList.add('border-t-8');
  } else if (y < 24) {
    RemoveBorderClasses(target, 'border-8');
    target.classList.add('border-8');
  } else {
    RemoveBorderClasses(target, 'border-b-8');
    target.classList.add('border-b-8');
  }
}

function HandleOnDrop(
  event: React.DragEvent,
  containers: Map<string, IContainerModel>,
  mainContainer: IContainerModel,
  addContainer: (index: number, type: string, parent: string) => void
): void {
  event.preventDefault();
  const type = event.dataTransfer.getData('type');
  let target: HTMLButtonElement = event.target as HTMLButtonElement;

  if (target.classList.contains('list-vertical-bar')) {
    target = target.parentElement as HTMLButtonElement;
  }

  RemoveBorderClasses(target);

  const targetContainer: IContainerModel | undefined = FindContainerById(
    containers,
    target.id
  );

  if (targetContainer === undefined) {
    throw new Error('[handleOnDrop] Tried to drop onto a unknown container!');
  }

  if (targetContainer === mainContainer) {
    // if the container is the root, only add type as child
    addContainer(
      targetContainer.children.length,
      type,
      targetContainer.properties.id
    );
    return;
  }

  const parent = FindContainerById(containers, targetContainer.properties.parentId);
  if (parent === null ||
      parent === undefined) {
    throw new Error('[handleDrop] Tried to drop into a child container without a parent!');
  }

  const rect = target.getBoundingClientRect();
  const y = event.clientY - rect.top; // y position within the element.

  // locate the hitboxes
  if (y < 12) {
    const index = parent.children.indexOf(targetContainer.properties.id);
    addContainer(
      index,
      type,
      parent.properties.id
    );
  } else if (y < 24) {
    addContainer(
      targetContainer.children.length,
      type,
      targetContainer.properties.id
    );
  } else {
    const index = parent.children.indexOf(targetContainer.properties.id);
    addContainer(
      index + 1,
      type,
      parent.properties.id
    );
  }
}

export function ElementsSidebar(props: IElementsSidebarProps): JSX.Element {
  // States
  const divRef = React.useRef<HTMLDivElement>(null);
  const [,height] = useSize(divRef);
  console.log(height);

  // Render
  const it = MakeRecursionDFSIterator(props.mainContainer, props.containers, 0, [0, 0], true);
  const containers = [...it];
  function Row({
    index, style
  }: {
    index: number
    style: React.CSSProperties
  }): JSX.Element {
    const { container, depth } = containers[index];
    const key = container.properties.id.toString();
    const text = container.properties.displayedText === key
      ? `${key}`
      : `${container.properties.displayedText}`;

    const isSelected = props.selectedContainer !== undefined &&
      props.selectedContainer !== null &&
      props.selectedContainer.properties.id === container.properties.id;

    return (
      ElementsListRow(
        key,
        index,
        container,
        depth,
        isSelected,
        style,
        text,
        props.containers,
        props.mainContainer,
        props.addContainer,
        props.selectContainer
      )
    );
  }

  return (
    <div className='flex flex-row h-full w-full' >
      {props.selectedExtendedSidebar === ExtendedSidebar.Property &&
        <div className='flex flex-1 flex-col w-64 border-r-2 border-slate-400'>
          <ContainerProperties
            containers ={props.containers}
            properties={props.selectedContainer?.properties}
            symbols={props.symbols}
            onChange={props.onPropertyChange}
          />
        </div>
      }
      <div className='flex w-64' ref={divRef}>
        <div className='w-6'>
          <ToggleSideBar
            title={Text({ textId: '@Properties' })}
            checked={props.selectedExtendedSidebar === ExtendedSidebar.Property}
            onClick={() => {
              const newValue = props.selectedExtendedSidebar !== ExtendedSidebar.Property
                ? ExtendedSidebar.Property
                : ExtendedSidebar.None;
              props.onExpandChange(newValue);
            }}
          />
        </div>
        <List
          itemCount={containers.length}
          itemSize={40}
          height={height}
          width={'100%'}
        >
          {Row}
        </List>
      </div>
    </div>
  );
}

function ElementsListRow(
  key: string,
  index: number,
  container: IContainerModel,
  depth: number,
  isSelected: boolean,
  style: React.CSSProperties,
  text: string,
  containers: Map<string, IContainerModel>,
  mainContainer: IContainerModel,
  addContainer: (index: number, type: string, parent: string) => void,
  selectContainer: (containerId: string) => void
): JSX.Element {
  const verticalBars: JSX.Element[] = [];

  let buttonClass = 'bg-blue-500 shadow-lg shadow-blue-500/60' +
  ' hover:bg-blue-600 hover:shadow-blue-500 text-slate-50 border-blue-600';
  let verticalBarClass = 'border-l-blue-400 group-hover:border-l-blue-300';
  if (!isSelected) {
    const isPair = (index & 1) === 0;
    if (isPair) {
      buttonClass = 'bg-slate-300/20 border-slate-500/50';
    } else {
      buttonClass = 'bg-slate-400/30 border-slate-500/80';
    }

    verticalBarClass = 'border-l-slate-400 group-hover:border-l-slate-300';
    buttonClass += ' hover:bg-slate-400 hover:shadow-slate-400';
  }

  for (let i = 0; i < depth; i++) {
    verticalBars.push(<span
      key={`${key}-${i}`}
      className={`list-vertical-bar h-full border-l-2 pr-1 ${verticalBarClass}`}
    ></span>);
  }

  return <div style={style}>
    <button type="button"
      className={`transition-all h-[38px]
      hover:shadow-lg elements-sidebar-row whitespace-pre 
      text-left text-sm font-medium flex items-center align-middle group 
      ${container.properties.type} ${buttonClass}`}
      id={key}
      key={key}
      title={container.properties.warning}
      onClick={() => { selectContainer(container.properties.id); }}
      onDrop={(event) => {
        HandleOnDrop(event, containers, mainContainer, addContainer);
      }}
      onDragOver={(event) => { HandleDragOver(event, mainContainer); }}
      onDragLeave={(event) => { HandleDragLeave(event); }}
    >
      {verticalBars}
      {text}
      {container.properties.warning.length > 0 &&
      <ExclamationTriangleIcon className='pl-2 w-7' />}
    </button>
    <hr className='border-t-2 h-[2px] border-t-slate-500'></hr>
  </div>;
}
