import { Fragment, useEffect, useRef, useState } from "react";
import {
  DndContext,
  closestCenter,
  PointerSensor,
  TouchSensor,
  useSensor,
  useSensors,
} from "@dnd-kit/core";
import {
  arrayMove,
  SortableContext,
  useSortable,
  verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { BiCopy, BiDotsVerticalRounded, BiX } from "react-icons/bi";
import {
  DengiButton,
  DengiYesOrNoDialog,
  LoadingSpinner,
} from "./trash-components";
import { motion, AnimatePresence, LayoutGroup } from "motion/react";
import {
  BiCheck,
  BiPlus,
  BiRedo,
  BiTime,
  BiTrash,
  BiUndo,
} from "react-icons/bi";
// import { ifstyle } from "../App";
import {
  apiFormatDate,
  convertApiToEditor,
  convertEditorToApi,
  formatDate,
} from "./dengi";
import {
  EditorPrefillRequest,
  EditorPublishRequest,
  EditorRemoveRequest,
} from "./editor_req_mgr";
import { API_BASE_URL } from "../App";
import { useWebHaptics } from "web-haptics/react";

// Configuration
const COLUMNS = [
  [
    {
      key: "position",
      label: "номер",
      width: "w-9",
      placeholder: "№",
      useinput: true,
    },
    {
      key: "subject",
      label: "предмет",
      width: "w-40",
      placeholder: "предмет",
      useinput: false,
    },
    {
      key: "room",
      label: "кабинет",
      width: "w-14",
      placeholder: "123",
      useinput: false,
    },
    {
      key: "time1",
      label: "начало",
      width: "w-16",
      placeholder: "00:00",
      useinput: true,
    },
    {
      key: "time2",
      label: "конец",
      width: "w-16",
      placeholder: "00:00",
      useinput: true,
    },
  ],
  [
    {
      key: "position",
      label: "номер",
      width: "w-9",
      placeholder: "№",
      useinput: true,
    },
    {
      key: "subject",
      label: "предмет",
      width: "w-40",
      placeholder: "предмет",
      useinput: false,
    },
    {
      key: "room",
      label: "кабинет",
      width: "w-14",
      placeholder: "123",
      useinput: false,
    },
  ],
  [
    {
      key: "position",
      label: "номер",
      width: "w-9",
      placeholder: "№",
      useinput: true,
    },
    {
      key: "time1",
      label: "начало",
      width: "w-16",
      placeholder: "00:00",
      useinput: true,
    },
    {
      key: "time2",
      label: "конец",
      width: "w-16",
      placeholder: "00:00",
      useinput: true,
    },
  ],
];

const INITIAL_DATA = [
  //   {
  //     id: "1",
  //     position: "1",
  //     subject: "Mathematics",
  //     time: "09:00",
  //     room: "A-1",
  //   },
  //   { id: "2", position: "2", subject: "Physics", time: "10:00", room: "B-2" },
  //   { id: "3", position: "3", subject: "Chemistry", time: "11:00", room: "A-3" },
  //   { id: "4", position: "4", subject: "Biology", time: "13:00", room: "C-1" },
];

function TableInput(props) {
  return props.useinput ? (
    <input {...{ ...props, useinput: undefined }} />
  ) : (
    <textarea {...{ ...props, useinput: undefined }} />
  );
}

function SortableRow({
  item,
  columns,
  onUpdate,
  onDelete,
  onDuplicate,
  setFocusEdit,
  setHoverEdit,
  pickmeMode,
}) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id: item.id });

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
    opacity: isDragging ? 0.5 : 1,
  };
  const { trigger } = useWebHaptics();
  return (
    <div
      ref={setNodeRef}
      style={style}
      className={`${
        pickmeMode ? "bg-pink-900/35" : "bg-zinc-800/35"
      } bg-grain rounded-lg mb-2`}
    >
      <div className="flex flex-col gap-2">
        <div className="flex">
          {columns.map((col) => (
            <TableInput
              key={col.key}
              useinput={col.useinput}
              type="text"
              value={item[col.key] || ""}
              wrap="hard"
              rows={1}
              onChange={(e) => onUpdate(item.id, col.key, e.target.value)}
              className={`px-1 py-3 text-center bg-transparent text-white border-r  ${
                pickmeMode ? "border-pink-400/50" : "border-zinc-700"
              }  last:border-r-0 last:rounded-tr-lg focus:outline-none focus:bg-gray-700/50 placeholder-gray-500 transition-colors hover:bg-gray-700/20 first:rounded-tl-lg max-h-min resize-none block min-w-0 grow ${
                col.width
              }`}
              placeholder={col.placeholder}
              onFocus={() => setFocusEdit(col.key)}
              onBlur={() => setFocusEdit(null)}
              onMouseOver={() => setHoverEdit(col.key)}
              onMouseLeave={() => setHoverEdit(null)}
            />
          ))}
        </div>
        <div className="flex justify-end w-full">
          <div
            {...attributes}
            {...listeners}
            className="min-w-12 min-h-12 transition-colors hover:bg-zinc-200 flex items-center justify-center cursor-move touch-none select-none hover:text-black font-black text-white rounded-xs rounded-bl-lg"
          >
            <BiDotsVerticalRounded size={24} />
          </div>
          <div className="w-full"></div>
          <button
            onClick={() => {
              onDuplicate(item.id);
              trigger([{ duration: 25 }], { intensity: 0.7 });
            }}
            className="min-w-12 min-h-12 transition-colors text-zinc-400 hover:bg-zinc-600/40 hover:text-zinc-300  flex items-center justify-center rounded-xs rounded-tl-md"
          >
            <BiCopy size={20} />
          </button>
          <button
            onClick={() => {
              onDelete(item.id);
              trigger([
                { duration: 40, intensity: 0.7 },
                { delay: 40, duration: 40, intensity: 0.7 },
                { delay: 40, duration: 40, intensity: 0.9 },
              ]);
            }}
            className="min-w-12 min-h-12 transition-colors   text-red-400 hover:bg-red-600/40 hover:text-red-300 active:bg-red-600/60 flex items-center justify-center rounded-xs rounded-br-md"
          >
            <BiX size={24} />
          </button>
        </div>
      </div>
    </div>
  );
}
async function getPrefilledRasp(tab, date, adminId) {
  let a = await EditorPrefillRequest(
    API_BASE_URL,
    tab,
    adminId,
    apiFormatDate(date),
  );
  if (a[0] == 1 && a[1]) {
    return convertApiToEditor(a[1]);
  }
}

export function TableEditor({
  initialData = INITIAL_DATA,
  onChange = () => {},
  selectedTab,
  columns = COLUMNS[selectedTab],
  setChangesWereMade,
  adminId,
  date,
  dengiCash,
  pickmeMode = false,
}) {
  const [items, setItems] = useState(initialData);
  const [undoItems, setUndoItems] = useState([]);
  const [redoItems, setRedoItems] = useState([]);
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
    let rejected = false;
    //console.log("cleanup");
    // cleanup on change
    setUndoItems([]);
    setRedoItems([]);
    setLoading(true);
    getPrefilledRasp(selectedTab, date, adminId).then((i) => {
      if (rejected) return;
      console.log(i || []);
      setItems(i || []);
      setLoading(false);
    });
    return () => {
      rejected = true;
    };
  }, [
    selectedTab,
    formatDate(date) /* NOTE: we need to format to rely only on DD-MM-YY,
   and forget about time at all*/,
  ]);

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: {
        distance: 8,
      },
    }),
    useSensor(TouchSensor, {
      activationConstraint: {
        delay: 200,
        tolerance: 8,
      },
    }),
  );

  const updateItems = (newItems) => {
    setItems(newItems);
    onChange?.(newItems);
  };

  useEffect(() => setChangesWereMade(undoItems.length > 0), [undoItems]);
  const handleDragEnd = (event) => {
    const { active, over } = event;
    if (over && active.id !== over.id) {
      const oldIndex = items.findIndex((item) => item.id === active.id);
      const newIndex = items.findIndex((item) => item.id === over.id);
      const newItems = arrayMove(items, oldIndex, newIndex);

      // Swap positions: assign old positions to new order
      const reorderedItems = newItems.map((item, idx) => ({
        ...item,
        position: items[idx].position, // Use position from original array at this index
      }));

      setUndoItems([items, ...undoItems]);
      updateItems(reorderedItems);
    }
  };
  const updateTimeoutRef = useRef({});
  const previousValueRef = useRef({});

  const handleUpdate = (id, field, value) => {
    const key = `${id}-${field}`;
    if (!updateTimeoutRef.current[key]) {
      const currentItem = items.find((item) => item.id === id);
      previousValueRef.current[key] = currentItem[field];
    }
    if (updateTimeoutRef.current[key]) {
      clearTimeout(updateTimeoutRef.current[key]);
    }
    const newItems = items.map((item) =>
      item.id === id ? { ...item, [field]: value } : item,
    );
    updateItems(newItems);
    updateTimeoutRef.current[key] = setTimeout(() => {
      // Create items array with the ORIGINAL value from before editing started
      const itemsWithOriginalValue = items.map((item) =>
        item.id === id
          ? { ...item, [field]: previousValueRef.current[key] }
          : item,
      );
      setUndoItems([itemsWithOriginalValue, ...undoItems]);

      // Clean up
      delete updateTimeoutRef.current[key];
      delete previousValueRef.current[key];
    }, 1000);
  };

  const handleUndo = () => {
    if (isLoading) return;
    setRedoItems([items, ...redoItems]);
    updateItems(undoItems[0]);
    setUndoItems(undoItems.slice(1));
  };
  const handleRedo = () => {
    if (isLoading) return;
    setUndoItems([items, ...undoItems]);
    updateItems(redoItems[0]);
    setRedoItems(redoItems.slice(1));
  };

  const handleDelete = (id) => {
    if (isLoading) return;
    const newItems = items.filter((item) => item.id !== id);
    setUndoItems([items, ...undoItems]);
    updateItems(newItems);
  };

  const handleDuplicate = (id) => {
    const index = items.findIndex((item) => item.id === id);
    if (index === -1) return;

    const itemToDuplicate = items[index];

    // Create new item with unique id
    const newItem = {
      ...itemToDuplicate,
      id: String(Date.now()), // or use uuid() or any unique id generator
    };

    // Insert the new item right after the duplicated item
    const newItems = [
      ...items.slice(0, index + 1),
      newItem,
      ...items.slice(index + 1),
    ];

    // Reassign positions based on the original order
    // The duplicate takes the next position, and everything shifts
    const reorderedItems = newItems.map((item, idx) => ({
      ...item,
      position: String(idx + 1), // or just (idx + 1) if position is a number
    }));

    setUndoItems([items, ...undoItems]);
    updateItems(reorderedItems);
  };

  const handleAddRow = () => {
    if (isLoading) return;
    const newId = String(Date.now());
    const sortedItems = [...items];
    sortedItems.sort((a, b) => a.position - b.position);
    const lastPosition =
      items.length > 0
        ? String(
            parseInt(sortedItems[sortedItems.length - 1].position || "0") + 1,
          )
        : "1";

    const newItem = { id: newId, position: lastPosition };
    columns.forEach((col) => {
      if (col.key !== "position") {
        newItem[col.key] = "";
      }
    });
    setUndoItems([items, ...undoItems]);
    updateItems([...items, newItem]);
    setTimeout(
      () =>
        window.scrollTo({
          top: document.documentElement.scrollHeight - window.innerHeight,
          left: window.scrollX, // preserve current X
          behavior: "smooth",
        }),
      100,
    );
  };

  const [focusEdit, setFocusEdit] = useState(null);
  const [hoverEdit, setHoverEdit] = useState(null);

  const [deleteDiag, setDeleteDiag] = useState(false);

  const handleClear = () => {
    if (isLoading) return;
    setUndoItems([items, ...undoItems]);
    updateItems([]);
  };

  const handleChangesDefault = () => {
    if (selectedTab != 0 || isLoading) return;
    setLoading(true);
    getPrefilledRasp(1, date, adminId).then((i) => {
      setUndoItems([items, ...undoItems]);
      setItems(i);
      setLoading(false);
    });
  };
  const handlePublish = () => {
    if (isLoading) return;
    Object.keys(updateTimeoutRef.current).forEach((i) => {
      clearTimeout(updateTimeoutRef.current[i]);
    });
    setUndoItems([]);
    setRedoItems([]);
    console.log(convertEditorToApi(items));
    dengiCash.fullClear(); // keep it clean
    setLoading(true);
    EditorPublishRequest(
      API_BASE_URL,
      selectedTab,
      adminId,
      apiFormatDate(date),
      JSON.stringify(convertEditorToApi(items)),
    ).then(() => {
      setLoading(false);
    });
  };
  const handleDeleteAll = () => {
    // if (isLoading) return; // WARN!
    setDeleteDiag(true);
  };

  const { trigger } = useWebHaptics();
  const handleDragOver = () => {
    trigger([{ duration: 25 }], { intensity: 0.7 });
  };

  let deleteDialogBusy = false; // NOTE: we don't need a state
  return (
    <>
      <div>
        <DengiYesOrNoDialog
          content={{
            message: "запись в базе данных будет удалена! продолжить?",
            yesBtn: "да",
            noBtn: "нет",
            yesIcon: <BiTrash />,
            noIcon: <BiX size={20} />,
            bgDissmis: true,
            yesBtnStyle: "bg-red-500! text-white! md:hover:bg-red-600!",
          }}
          onAnswer={(answ) => {
            if (deleteDialogBusy) return;
            if (!answ) setDeleteDiag(false);
            else {
              dengiCash.fullClear();
              // scary
              deleteDialogBusy = true;
              EditorRemoveRequest(
                API_BASE_URL,
                selectedTab,
                adminId,
                apiFormatDate(date),
              ).then(() => {
                deleteDialogBusy = false; // for 100% sure
                setDeleteDiag(false);
                location.hash = "/app";
              });
            }
          }}
          show={deleteDiag}
        />
      </div>
      <div className="w-full overflow-hidden my-1 flex flex-col gap-3">
        <div className="flex gap-2 flex-wrap">
          <DengiButton onClick={handleUndo} disabled={undoItems.length == 0}>
            <BiUndo />
          </DengiButton>
          <DengiButton onClick={handleRedo} disabled={redoItems.length == 0}>
            <BiRedo />
          </DengiButton>  
          <AnimatePresence mode="popLayout">
            {selectedTab == 0 && (
              <motion.div exit={{ opacity: 0 }}>
                <DengiButton
                  className="border-red-600! md:not-disabled:not-hover:border-red-400!"
                  onClick={handleChangesDefault}
                >
                  <BiTime />
                  убрать&nbsp;зам.
                </DengiButton>
              </motion.div>
            )}
          </AnimatePresence>
          <motion.div layout="position" className="bg-black">
            <DengiButton
              onClick={handleClear}
              className="border-red-600! md:not-disabled:not-hover:border-red-400!"
            >
              <BiTrash />
              очистить
            </DengiButton>
          </motion.div>
        </div>
        {!isLoading && (
          <div>
            <div className="flex w-full justify-around items-center mb-2">
              {columns.map((col, i) => (
                <Fragment key={col.key + i}>
                  <div
                    key={col.key + i}
                    className={
                      `max-${col.width} transition-colors text-` +
                      ((focusEdit == null && hoverEdit == null) ||
                      focusEdit == col.key ||
                      hoverEdit == col.key
                        ? "white"
                        : "zinc-600")
                    }
                    // onMouseOver={() => setHoverEdit(col.key)}
                    // onMouseLeave={() => setHoverEdit(null)}
                  >
                    {col.label}
                  </div>

                  <span
                    key={col.key + i + "divider"}
                    className={`w-4 h-[1px] ${
                      pickmeMode ? "bg-pink-600" : "bg-zinc-600"
                    } -rotate-45 last:hidden`}
                  />
                </Fragment>
              ))}
            </div>
            <div className="max-w-3xl mx-auto">
              <DndContext
                sensors={sensors}
                collisionDetection={closestCenter}
                onDragEnd={handleDragEnd}
                onDragOver={handleDragOver}
              >
                <SortableContext
                  items={items.map((item) => item.id)}
                  strategy={verticalListSortingStrategy}
                > 
                  <div>
                    <AnimatePresence mode={items.length > 0 ? "sync" : "wait"}>
                      {items.map((item) => (
                        <motion.div
                          initial={{
                            x: -15,
                            filter: "blur(5px)",
                            opacity: 0,
                          }}
                          animate={{ x: 0, filter: "blur(0px)", opacity: 1 }}
                          exit={{ x: 15, filter: "blur(5px)", opacity: 0 }}
                          key={item.id}
                        >
                          <SortableRow
                            item={item}
                            columns={columns}
                            onUpdate={handleUpdate}
                            onDelete={handleDelete}
                            onDuplicate={handleDuplicate}
                            setFocusEdit={setFocusEdit}
                            setHoverEdit={setHoverEdit}
                            pickmeMode={pickmeMode}
                          />
                        </motion.div>
                      ))}
                      {items.length === 0 && (
                        <motion.div
                          initial={{
                            filter: "blur(5px)",
                            opacity: 0,
                            scale: 0.8,
                          }}
                          animate={{
                            filter: "blur(0px)",
                            opacity: 1,
                            scale: 1,
                          }}
                          className={`border-2 border-dashed ${
                            pickmeMode ? "border-pink-700" : "border-gray-700"
                          } rounded-lg p-12 text-center text-gray-500   `}
                        >
                          пусто
                        </motion.div>
                      )}
                    </AnimatePresence>
                  </div>
                </SortableContext>
              </DndContext>
            </div>
          </div>
        )}
        {isLoading && (
          <div className="w-full flex items-center justify-center py-3">
            <LoadingSpinner size={35} />
          </div>
        )}
        <motion.div className="flex w-full justify-end gap-2 fixed pr-20 bottom-2 left-0">
          <DengiButton
            onClick={handleDeleteAll}
            className="border-red-600! md:not-disabled:not-hover:border-red-400!"
          >
            <BiTrash />
            удалить из бд
          </DengiButton>
          <DengiButton onClick={handleAddRow}>
            <BiPlus />
          </DengiButton>
          <DengiButton onClick={handlePublish}>
            <BiCheck />
            сохранить
          </DengiButton>
        </motion.div>
      </div>
    </>
  );
}
