import styled from '@emotion/styled';
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
import { useContext, useRef } from 'react';
import { useRecoilCallback } from 'recoil';
import { Key } from 'ts-key-enum';

import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope';
import { RecordBoardHeader } from '@/object-record/record-board/components/RecordBoardHeader';
import { RecordBoardStickyHeaderEffect } from '@/object-record/record-board/components/RecordBoardStickyHeaderEffect';
import { RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-board/constants/RecordBoardClickOutsideListenerId';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection';
import { RecordBoardColumn } from '@/object-record/record-board/record-board-column/components/RecordBoardColumn';
import { RecordBoardScope } from '@/object-record/record-board/scopes/RecordBoardScope';
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useScrollRestoration } from '~/hooks/useScrollRestoration';

const StyledContainer = styled.div`
  display: flex;
  flex: 1;
  flex-direction: row;
`;

const StyledColumnContainer = styled.div`
  display: flex;
  & > *:not(:first-of-type) {
    border-left: 1px solid ${({ theme }) => theme.border.color.light};
  }
`;

const StyledContainerContainer = styled.div`
  display: flex;
  flex-direction: column;
  height: 100%;
`;

const StyledBoardContentContainer = styled.div`
  display: flex;
  flex-direction: column;
  height: calc(100% - 48px);
`;

const RecordBoardScrollRestoreEffect = () => {
  useScrollRestoration();
  return null;
};

export const RecordBoard = () => {
  const { updateOneRecord, selectFieldMetadataItem, recordBoardId } =
    useContext(RecordBoardContext);
  const boardRef = useRef<HTMLDivElement>(null);

  const visibleRecordGroupIds = useRecoilComponentValueV2(
    visibleRecordGroupIdsComponentSelector,
  );

  const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2(
    recordIndexRowIdsByGroupComponentFamilyState,
  );

  const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
    recordIndexAllRowIdsComponentState,
  );

  const { resetRecordSelection, setRecordAsSelected } =
    useRecordBoardSelection(recordBoardId);

  useListenClickOutsideV2({
    excludeClassNames: [
      'bottom-bar',
      'action-menu-dropdown',
      'command-menu',
      'modal-backdrop',
    ],
    listenerId: RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID,
    refs: [boardRef],
    callback: resetRecordSelection,
  });

  const selectAll = useRecoilCallback(
    ({ snapshot }) =>
      () => {
        const allRecordIds = getSnapshotValue(
          snapshot,
          recordIndexAllRowIdsState,
        );

        for (const recordId of allRecordIds) {
          setRecordAsSelected(recordId, true);
        }
      },
    [recordIndexAllRowIdsState, setRecordAsSelected],
  );

  useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table);
  useScopedHotkeys('ctrl+a,meta+a', selectAll, ActionBarHotkeyScope.ActionBar);

  useScopedHotkeys(
    Key.Escape,
    resetRecordSelection,
    ActionBarHotkeyScope.ActionBar,
  );

  const handleDragEnd: OnDragEndResponder = useRecoilCallback(
    ({ snapshot }) =>
      (result) => {
        if (!result.destination) return;

        const draggedRecordId = result.draggableId;
        const sourceRecordGroupId = result.source.droppableId;
        const destinationRecordGroupId = result.destination.droppableId;
        const destinationIndexInColumn = result.destination.index;

        if (!destinationRecordGroupId || !selectFieldMetadataItem) return;

        const recordGroup = getSnapshotValue(
          snapshot,
          recordGroupDefinitionFamilyState(destinationRecordGroupId),
        );

        if (!recordGroup) return;

        const destinationRecordByGroupIds = getSnapshotValue(
          snapshot,
          recordIndexRowIdsByGroupFamilyState(destinationRecordGroupId),
        );
        const otherRecordIdsInDestinationColumn =
          sourceRecordGroupId === destinationRecordGroupId
            ? destinationRecordByGroupIds.filter(
                (recordId) => recordId !== draggedRecordId,
              )
            : destinationRecordByGroupIds;

        const recordBeforeId =
          otherRecordIdsInDestinationColumn[destinationIndexInColumn - 1];
        const recordBefore = recordBeforeId
          ? getSnapshotValue(snapshot, recordStoreFamilyState(recordBeforeId))
          : null;

        const recordAfterId =
          otherRecordIdsInDestinationColumn[destinationIndexInColumn];
        const recordAfter = recordAfterId
          ? getSnapshotValue(snapshot, recordStoreFamilyState(recordAfterId))
          : null;

        const draggedRecordPosition = getDraggedRecordPosition(
          recordBefore?.position,
          recordAfter?.position,
        );

        updateOneRecord({
          idToUpdate: draggedRecordId,
          updateOneRecordInput: {
            [selectFieldMetadataItem.name]: recordGroup.value,
            position: draggedRecordPosition,
          },
        });
      },
    [
      recordIndexRowIdsByGroupFamilyState,
      selectFieldMetadataItem,
      updateOneRecord,
    ],
  );

  // FixMe: Check if we really need this as it depends on the times it takes to update the view groups
  // if (isPersistingViewGroups) {
  //   // TODO: Add skeleton state
  //   return null;
  // }

  return (
    <RecordBoardScope
      recordBoardScopeId={getScopeIdFromComponentId(recordBoardId)}
      onColumnsChange={() => {}}
      onFieldsChange={() => {}}
    >
      <RecordBoardComponentInstanceContext.Provider
        value={{ instanceId: recordBoardId }}
      >
        <ScrollWrapper contextProviderName="recordBoard">
          <RecordBoardStickyHeaderEffect />
          <StyledContainerContainer>
            <RecordBoardHeader />
            <StyledBoardContentContainer>
              <StyledContainer ref={boardRef}>
                <DragDropContext onDragEnd={handleDragEnd}>
                  <StyledColumnContainer>
                    {visibleRecordGroupIds.map((recordGroupId) => (
                      <RecordBoardColumn
                        key={recordGroupId}
                        recordBoardColumnId={recordGroupId}
                      />
                    ))}
                  </StyledColumnContainer>
                </DragDropContext>
              </StyledContainer>
              <RecordBoardScrollRestoreEffect />
              <DragSelect
                dragSelectable={boardRef}
                onDragSelectionStart={resetRecordSelection}
                onDragSelectionChange={setRecordAsSelected}
              />
            </StyledBoardContentContainer>
          </StyledContainerContainer>
        </ScrollWrapper>
      </RecordBoardComponentInstanceContext.Provider>
    </RecordBoardScope>
  );
};
