import React, { useCallback, useEffect, useRef, useState } from 'react';
import Axios from 'axios';
import { AsyncThunk } from '@reduxjs/toolkit';
import { useDrop } from 'react-dnd';
import CardBox from '../CardBox';
import CardBoxModal from '../CardBoxModal';
import LoadingSpinner from '../LoadingSpinner';
import CardBoxComponentEmpty from '../CardBoxComponentEmpty';
import KanbanCard from './KanbanCard';
import { useAppDispatch, useAppSelector } from '../../stores/hooks';

type Props = {
  column: { id: string; label: string };
  entityName: string;
  columnFieldName: string;
  showFieldName: string;
  filtersQuery: string;
  deleteThunk: AsyncThunk<any, any, any>;
  updateThunk: AsyncThunk<any, any, any>;
};

type DropResult = {
  sourceColumn: { id: string; label: string };
  item: any;
};

const perPage = 10;

const KanbanColumn = ({
  column,
  entityName,
  columnFieldName,
  showFieldName,
  filtersQuery,
  deleteThunk,
  updateThunk,
}: Props) => {
  const dispatch = useAppDispatch();
  const currentUser = useAppSelector((state) => state.auth.currentUser);
  const listInnerRef = useRef<HTMLDivElement | null>(null);

  const [currentPage, setCurrentPage] = useState(0);
  const [count, setCount] = useState(0);
  const [data, setData] = useState<any[]>([]);
  const [loading, setLoading] = useState(false);
  const [itemIdToDelete, setItemIdToDelete] = useState('');

  const [{ dropResult }, drop] = useDrop(
    () => ({
      accept: 'box',
      drop: ({ item, column: sourceColumn }: { item: any; column: { id: string; label: string } }) => {
        if (sourceColumn.id === column.id) return;

        dispatch(
          updateThunk({
            id: item.id,
            data: {
              [columnFieldName]: column.id,
            },
          }),
        ).then(() => {
          const movedItem = {
            ...item,
            [columnFieldName]: column.id,
          };

          setData((prevState) => [movedItem, ...prevState.filter((record) => record.id !== item.id)]);
          setCount((prevState) => prevState + 1);
        });

        return { sourceColumn, item };
      },
      collect: (monitor) => ({
        dropResult: monitor.getDropResult() as DropResult | null,
      }),
    }),
    [column.id, columnFieldName, dispatch, updateThunk],
  );

  const loadData = useCallback(
    async (page: number, filters = '') => {
      const query = `?page=${page}&limit=${perPage}&field=createdAt&sort=desc&${columnFieldName}=${column.id}&${filters}`;
      setLoading(true);

      try {
        const res = await Axios.get(`/${entityName}${query}`);
        setData((prevState) => (page === 0 ? res.data.rows : [...prevState, ...res.data.rows]));
        setCount(res.data.count);
        setCurrentPage(page);
      } catch (error) {
        console.error(`Failed to load ${entityName} kanban column ${column.id}`, error);
      } finally {
        setLoading(false);
      }
    },
    [column.id, columnFieldName, entityName],
  );

  useEffect(() => {
    if (!currentUser) return;
    loadData(0, filtersQuery);
  }, [currentUser, loadData, filtersQuery]);

  useEffect(() => {
    if (!dropResult?.sourceColumn || dropResult.sourceColumn.id !== column.id) {
      return;
    }

    setData((prevState) => prevState.filter((item) => item.id !== dropResult.item.id));
    setCount((prevState) => Math.max(prevState - 1, 0));
  }, [column.id, dropResult]);

  const onScroll = () => {
    if (!listInnerRef.current || loading) return;

    const { scrollTop, scrollHeight, clientHeight } = listInnerRef.current;
    if (scrollTop + clientHeight >= scrollHeight - 12 && data.length < count) {
      loadData(currentPage + 1, filtersQuery);
    }
  };

  const onDeleteConfirm = () => {
    if (!itemIdToDelete) return;

    dispatch(deleteThunk(itemIdToDelete))
      .then((res) => {
        if (res.meta.requestStatus === 'fulfilled') {
          setItemIdToDelete('');
          loadData(0, filtersQuery);
        }
      })
      .catch((error) => {
        console.error(`Failed to delete ${entityName} item ${itemIdToDelete}`, error);
      })
      .finally(() => {
        setItemIdToDelete('');
      });
  };

  return (
    <>
      <CardBox hasComponentLayout className='w-[280px] overflow-hidden rounded-2xl border border-gray-200/80 bg-gray-50/80 shadow-none dark:border-dark-700 dark:bg-dark-900/70'>
        <div className='flex items-center justify-between border-b border-gray-200/80 px-4 py-4 dark:border-dark-700'>
          <div>
            <p className='text-xs font-semibold uppercase tracking-[0.18em] text-gray-400 dark:text-gray-500'>
              Stage
            </p>
            <p className='mt-1 text-sm font-semibold text-gray-900 dark:text-white'>{column.label}</p>
          </div>
          <div className='rounded-full bg-white px-3 py-1 text-xs font-semibold text-gray-600 shadow-sm dark:bg-dark-800 dark:text-gray-300'>
            {count}
          </div>
        </div>
        <div
          ref={(node) => {
            drop(node);
            listInnerRef.current = node;
          }}
          className='max-h-[560px] flex-1 space-y-3 overflow-y-auto p-3'
          onScroll={onScroll}
        >
          {data.map((item) => (
            <KanbanCard
              key={item.id}
              item={item}
              column={column}
              showFieldName={showFieldName}
              entityName={entityName}
              setItemIdToDelete={setItemIdToDelete}
            />
          ))}

          {!loading && !data.length && (
            <CardBoxComponentEmpty
              compact
              title={`No ${entityName.replace(/_/g, ' ')} in ${column.label}`}
              description='When records move into this stage, they will appear here automatically.'
            />
          )}

          {loading && (
            <LoadingSpinner
              compact
              label={`Loading ${column.label.toLowerCase()} stage`}
              detail='Pulling the latest items for this lane.'
            />
          )}
        </div>
      </CardBox>
      <CardBoxModal
        title='Please confirm'
        buttonColor='info'
        buttonLabel={loading ? 'Deleting...' : 'Confirm'}
        isActive={!!itemIdToDelete}
        onConfirm={onDeleteConfirm}
        onCancel={() => setItemIdToDelete('')}
      >
        <p>Are you sure you want to delete this item?</p>
      </CardBoxModal>
    </>
  );
};

export default KanbanColumn;
