import React, { useRef, useEffect, FC, useState } from "react";
import * as pdfjs from "pdfjs-dist";
import styled from "styled-components";
import Point from "../Point/Point";
import {
  calculateFitToScreen,
  calculateSetPosition,
  calculateShift,
  calculateZoom,
  zoomScale,
} from "./readerHelper";
import { MapNavigator } from "../MapNavigator";
import { PDFPageProxy } from "pdfjs-dist";
import { PageViewport } from "pdfjs-dist/types/src/display/display_utils";
import { IElementType } from "../../definitions/element";
import { Aligment } from "venice-ui";
import {
  ADDITIONAL_TYPES,
  IAddtionalElements,
  TAdditionalType,
} from "../../definitions/additional";
import { AdditionalElements } from "../AdditionalElements/AdditionalElements";
import { ISpoolCanvas } from "../../definitions/spools";
import { Spool } from "../Spool/Spool";
import { IFocusedProps } from "./Section";
import { debounce, throttle } from "lodash";

interface PdfReaderProps {
  pdf: pdfjs.PDFDocumentProxy;
  pageNumber: number;
  addElementAction: any;
  addMapElement: any;
  points: IElementType[];
  spools: ISpoolCanvas[];
  mapElements: IAddtionalElements[];
  activeItem: string;
  showDetails: (
    id: string,
    isMapElement: boolean,
    isReference: boolean
  ) => void;
  showSpoolDetails: (spoolID: string) => void;
  hideDetails: () => void;
  saveChanges: (
    element: IElementType | IAddtionalElements,
    isSource: boolean
  ) => void;
  saveSpoolChanges: (spool: ISpoolCanvas) => void;
  drawMode: TAdditionalType;
  editMode: boolean;
  setScale: (scale: number) => void;
  scaleDirection: string;
  handleShortcut: (key: string, elementID: string, elementType: string) => void;
  action: boolean;
  setAction: () => void;
  cancelAction: () => void;
  focused: IFocusedProps;
  updateScale: (direction: string) => void;
  inputFocused:string
}

interface IEnvProps {
  context: CanvasRenderingContext2D | null;
  page: PDFPageProxy | null;
  viewport: PageViewport | null;
}

interface IWorkspacePosition {
  width: number;
  height: number;
  maxLeft: number;
  maxTop: number;
  x: number;
  y: number;
  scale: number;
}

interface ICanvasWrapperStyle {
  height: string;
}
const CanvasWrapper = styled.div<ICanvasWrapperStyle>`
  width: 100%;
  height: ${(p) => p.height};
  overflow: hidden;
  position: relative;
  align-items: flex-start;
  background-color: #f5f5f5;
  box-shadow: 0 1px 3px rgb(0 0 0 / 12%), 0 1px 2px rgb(0 0 0 / 24%);
`;


const WorkArea = styled.div`
  position: absolute;
`;
interface IWorkLayer {
  width: number;
  height: number;
}
const WorkLayer = styled.div<IWorkLayer>`
  position: absolute;
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
  z-index: 30;
`;

const Canvas = styled.canvas`
  image-orientation: from-image;
`;

const PdfReader: FC<PdfReaderProps> = ({
  pdf,
  pageNumber,
  addElementAction,
  addMapElement,
  points,
  spools,
  mapElements,
  activeItem,
  showDetails,
  showSpoolDetails,
  hideDetails,
  saveChanges,
  saveSpoolChanges,
  drawMode,
  editMode,
  setScale,
  scaleDirection,
  handleShortcut,
  action,
  setAction,
  cancelAction,
  focused,
  updateScale,
  inputFocused
}: PdfReaderProps) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const workRef = useRef<HTMLDivElement>(null);
  const layerRef = useRef<HTMLDivElement>(null);
  const workWindowRef = useRef<HTMLDivElement>(null);
  let canvas = canvasRef.current;
  let workspace = workRef.current;
  let workspaceWindow = workWindowRef.current;
  let workLayer = layerRef.current;

  const [envProps, _setEnvProps] = useState<IEnvProps>({
    page: null,
    viewport: null,
    context: null,
  });
  const envPropsRef = useRef(envProps);
  const setEnvProps = (data: IEnvProps) => {
    envPropsRef.current = data;
    _setEnvProps(data);
  };

  const [addtionalMode, _setAdditionalMode] = useState(drawMode);
  const addtionalModeRef = useRef(addtionalMode);
  const setAdditionalMode = (mode: TAdditionalType) => {
    addtionalModeRef.current = mode;
    _setAdditionalMode(mode);
  };

  const [actionState, _setActionState] = useState(action);
  const actionStateRef = useRef(actionState);
  const setActionState = (x: boolean) => {
    actionStateRef.current = x;
    _setActionState(x);
  };

  useEffect(() => {
    setActionState(action);
  }, [action]);

  useEffect(() => {
    setAdditionalMode(drawMode);
  }, [drawMode]);

  useEffect(() => {
    if (scaleDirection !== "none") {
      zoomMap(scaleDirection);
    }
  }, [scaleDirection]);

  const [editMapMode, _setEditMapMode] = useState(editMode);
  const editModeRef = useRef(editMapMode);
  const setEditMapMode = (edit: boolean) => {
    editModeRef.current = edit;
    _setEditMapMode(edit);
  };

  const [renderInProgress, _setRenderInProgress] = useState(false);
  const renderInProgressRef = useRef(renderInProgress);
  const setRenderInProgress = (data: boolean) => {
    renderInProgressRef.current = data;
    _setRenderInProgress(data);
  };
  const [readyToRender, changeReadyToRender] = useState(true);

  const [workspacePosition, _updateWorkspacePosition] =
    useState<IWorkspacePosition>({
      width: 0,
      height: 0,
      maxLeft: 0,
      maxTop: 0,
      x: 0,
      y: 0,
      scale: 1,
    });

  const workspacePositionRef = useRef(workspacePosition);
  const updateWorkspacePosition = (data: IWorkspacePosition) => {
    workspacePositionRef.current = data;
    _updateWorkspacePosition(data);
  };

  const scaleRef = useRef(workspacePositionRef.current.scale);
  const previewResetPendingRef = useRef(false);

  const previewScale = (next: number) => {
    if (workRef.current) {
      //tutaj bediz etrzeba wspolrzedne jeszcze omotac
      workRef.current.style.transformOrigin = "center center";
      workRef.current.style.transform = `scale(${next})`;
    }
  };

  const commitScaleDebounced = useRef(
    debounce((finalScale: number) => {
      previewResetPendingRef.current = true;
      setZoomMap(finalScale); // pełny render + aktualizacja stanu
    }, 120)
  ).current;

  let dragHelper = false;
  let clickHelper = false;
  let ctrlHelper = false;

  const startPoint = {
    x: 0,
    y: 0,
  };
  const endPoint = {
    x: 0,
    y: 0,
  };

  let elementTopPosition = 0;

  const [elementHeight, updatEelementHeight] = useState(0);

  const setHeight = () => {
    const viewPortHeight = document.defaultView?.visualViewport?.height;
    const elem = document.getElementById("work_panel");
    elementTopPosition = elem!.getBoundingClientRect().top;
    updatEelementHeight(viewPortHeight! - elementTopPosition- 10);
  };

  useEffect(() => {
    setRenderInProgress(true);
    setHeight();
  }, []);

  const setPosition = (dimentionX: number, dimentionY: number) => {
    const newPosition = calculateSetPosition(
      dimentionX,
      dimentionY,
      workspacePositionRef.current
    );
    updateWorkspacePosition({
      ...workspacePositionRef.current,
      x: newPosition.x,
      y: newPosition.y,
    });
    startPoint.x = endPoint.x;
    startPoint.y = endPoint.y;
  };

  const startMove = (event: any) => {
    if (event.ctrlKey) {
      ctrlHelper = true;
    }
    dragHelper = true;
    clickHelper = true;
    const parent = event.target.parentNode.parentNode;
    startPoint.x = event.pageX - parent.offsetLeft;
    startPoint.y = event.pageY - parent.offsetTop;
  };

  const move = (event: any) => {
    if (dragHelper) {
      const parent = event.target.parentNode.parentNode;
      clickHelper = false;
      endPoint.x = event.pageX - parent.offsetLeft;
      endPoint.y = event.pageY - parent.offsetTop;
      if (!ctrlHelper) {
        setPosition(endPoint.x - startPoint.x, endPoint.y - startPoint.y);
      }
    }
  };

  const endMove = (event: any) => {
    if (action) {
      cancelAction();
    }
    setTimeout(() => (clickHelper = false), 10);
    dragHelper = false;
    ctrlHelper = false;
  };

  const shiftMap = (direction: string) => {
    const shift = calculateShift(direction);
    setPosition(shift.x, shift.y);
  };

  const setZoomMap = async (newScale: number) => {
    const currentWorkspacePosition = workspacePositionRef.current;
    const { x, y, scale } = currentWorkspacePosition;
    const newPositionX = Math.round((x / scale) * newScale);
    const newPositionY = Math.round((y / scale) * newScale);

    // currentWorkspacePosition.scale = newScale;
    scaleRef.current = newScale;

    updateWorkspacePosition({
      ...currentWorkspacePosition,
      x: newPositionX,
      y: newPositionY,
      scale: newScale,
    });

    const viewport = await envPropsRef.current.page!.getViewport({
      scale: newScale,
    });

    setEnvProps({
      ...envPropsRef.current,
      viewport: viewport,
    });
    setScale(newScale);
  };

  const zoomMap = async (direction: string) => {
    const currentWorkspacePosition = workspacePositionRef.current;
    const { x, y, scale } = currentWorkspacePosition;
      
    let newScale = 1;
    if (direction === "fit") {
      newScale = calculateFitToScreen(
        workspaceWindow!.offsetWidth,
        workspacePositionRef.current.width,
        scale
      );
    } else if (direction !== "reset") {
      newScale = calculateZoom(direction, scale);
    }
    const newPositionX =
      direction === "fit" ? 0 : Math.round((x / scale) * newScale);
    const newPositionY =
      direction === "fit" ? 0 : Math.round((y / scale) * newScale);

    currentWorkspacePosition.scale = newScale;

    updateWorkspacePosition({
      ...currentWorkspacePosition,
      x: newPositionX,
      y: newPositionY,
      scale: newScale,
    });

    const viewport = await envPropsRef.current.page!.getViewport({
      scale: newScale,
    });

    setEnvProps({
      ...envPropsRef.current,
      viewport: viewport,
    });
    setScale(newScale);
  };

  //ToDo refator after finish
  function addPoint(event: MouseEvent) {
    event.preventDefault();
    if (clickHelper) {
      const scale = workspacePositionRef.current.scale;
      const cordinates = {
        x: event.offsetX,
        y: event.offsetY,
      };
      if (editModeRef.current) {
        // if (addtionalModeRef.current === "") {
        //   if (actionStateRef.current) {
        //     cancelAction();
        //   } else {
        //     hideDetails();
        //   }
        // }
        if (
          addtionalModeRef.current === ADDITIONAL_TYPES.dot ||
          addtionalModeRef.current === ADDITIONAL_TYPES.triage
        ) {
          addMapElement(
            cordinates.x / scale,
            cordinates.y / scale,
            addtionalModeRef.current
          );
        } else if (addtionalModeRef.current === ADDITIONAL_TYPES.edge) {
          addMapElement(
            cordinates.x / scale,
            cordinates.y / scale,
            addtionalModeRef.current
          );
        } else if (addtionalModeRef.current === ADDITIONAL_TYPES.cloud) {
          addMapElement(
            cordinates.x / scale,
            cordinates.y / scale,
            addtionalModeRef.current
          );
        }
      } else {
        addElementAction(cordinates.x / scale, cordinates.y / scale, "joint");
      }
    }
  }

  const resetSelction = (event: any) => {
    event.preventDefault();
    if (clickHelper) {
      if (actionStateRef.current) {
        cancelAction();
      } else {
        hideDetails();
      }
    }
  };

  const setEnv = async () => {
    const page = await pdf.getPage(pageNumber);
    const viewport = await page.getViewport({
      scale: workspacePositionRef.current.scale,
      rotation: page.rotate,
    });

    const context = canvas!.getContext("2d");

    setEnvProps({
      ...envPropsRef.current,
      page: page,
      viewport: viewport,
      context: context,
    });
  };

  const drawSchema = async () => {
    if (!renderInProgressRef.current || !readyToRender) return;

    const { context, viewport, page } = envPropsRef.current;

    if (!canvas || !viewport || !page) return;
    const { width, height } = viewport;
    canvas.height = height;
    canvas.width = width;

    const maxLeft = Math.min(0, -(canvas.width - workspaceWindow!.offsetWidth));
    const maxTop = Math.min(
      0,
      -(canvas.height - workspaceWindow!.offsetHeight)
    );

    const updatedWorkspacePosition = {
      ...workspacePositionRef.current,
      height,
      width,
      maxLeft,
      maxTop,
    };

    updateWorkspacePosition(updatedWorkspacePosition);

    context!.clearRect(0, 0, canvas.width, canvas.height);

    const renderContext = {
      canvasContext: context!,
      viewport: viewport,
    };

    if (renderInProgressRef.current && readyToRender) {
      changeReadyToRender(false);

      await page.render(renderContext).promise.then(() => {
        setRenderInProgress(false);
        changeReadyToRender(true);
      });

      if (previewResetPendingRef.current && workRef.current) {
        workRef.current.style.transform = `scale(1)`;
        previewResetPendingRef.current = false;
      }
    }
  };
  const handleWheel = (e: WheelEvent) => {
    // e.preventDefault();
    // const direction = e.deltaY > 0 ? "down" : "up";
    // const current = scaleRef.current;
    // const next = calculateZoom(direction, current);
    // if (next === current) return;
    // scaleRef.current = next;
    // previewScale(next);
    // commitScaleDebounced(next);
  };

  const handleResize = async () => {
    const viewport = await envPropsRef.current!.page!.getViewport({
      scale: workspacePositionRef.current.scale,
      rotation: envPropsRef.current!.page!.rotate,
    });

    setHeight();
    setEnvProps({
      ...envPropsRef.current,
      viewport: viewport,
    });
  };

  useEffect(() => {
    workLayer = workRef.current;
    canvas = canvasRef.current;
    workspace = workRef.current;
    workspaceWindow = workWindowRef.current;

    if (canvas && workspace && workLayer) {
      workLayer.addEventListener("mousedown", startMove);
      workLayer.addEventListener("mousemove", move);
      workLayer.addEventListener("mouseup", endMove);
      workLayer.addEventListener("mouseleave", endMove);
      workLayer.addEventListener("contextmenu", addPoint);
      workLayer.addEventListener("click", resetSelction);
      window.addEventListener("resize", handleResize);
      if (layerRef.current) {
        layerRef.current.addEventListener("wheel", handleWheel, {
          passive: false,
        });
      }
      // }
    }
    return () => {
      if (canvas && workspace && workLayer) {
        workLayer.removeEventListener("mousedown", startMove);
        workLayer.removeEventListener("mousemove", move);
        workLayer.removeEventListener("mouseup", endMove);
        workLayer.removeEventListener("mouseleave", endMove);
        workLayer.removeEventListener("contextmenu", addPoint);
        workLayer.removeEventListener("click", resetSelction);
        window.removeEventListener("resize", handleResize);
        if (layerRef.current) {
          layerRef.current.removeEventListener("wheel", handleWheel);
        }
        commitScaleDebounced.cancel();
      }
    };
  }, []);

  useEffect(() => {
    setRenderInProgress(true);
    setEnv();
  }, [pageNumber, pdf]);

  useEffect(() => {
    setRenderInProgress(true);
    drawSchema();
  }, [envPropsRef.current]);

  useEffect(() => {
    setAdditionalMode(drawMode);
  }, [drawMode]);

  useEffect(() => {
    setEditMapMode(editMode);
  }, [editMode]);

  return (
    <Aligment align="flex-start">
      <CanvasWrapper
        ref={workWindowRef}
        id="work_panel"
        height={elementHeight + "px"}
      >
        <WorkArea
          style={{
            left: workspacePositionRef.current.x + "px",
            top: workspacePositionRef.current.y + "px",
          }}
          ref={workRef}
        >
          <Canvas
            id="canvasPdf"
            ref={canvasRef}
            width={window.innerWidth}
            height={window.innerHeight}
          />
          <WorkLayer
            ref={layerRef}
            width={window.innerWidth}
            height={window.innerHeight}
          />

          {mapElements.length > 0 && (
            <>
              {mapElements.map((item) => (
                <AdditionalElements
                  key={item.id}
                  element={item}
                  editMode={editMode}
                  active={activeItem === item.id}
                  scale={workspacePositionRef.current.scale}
                  pdfHeight={envPropsRef.current?.viewport?.height}
                  pdfWidth={envPropsRef.current?.viewport?.width}
                  showDetails={showDetails}
                  saveChanges={saveChanges}
                  handleShortcut={handleShortcut}
                  setAction={setAction}
                  cancelAction={cancelAction}
                  isAction={action}
                  workspaceTop={workspacePositionRef.current.y}
                  workspaceLeft={workspacePositionRef.current.x}
                  focused={
                    focused.type === "element" && focused.id === item.id
                      ? true
                      : false
                  }
                  inputFocused={inputFocused}
                />
              ))}
            </>
          )}
          {points.length > 0 &&
            points.map((point) => (
              <Point
                element={point}
                key={point.id! as string}
                showDetails={showDetails}
                active={activeItem === point.id}
                scale={workspacePositionRef.current.scale}
                saveChanges={saveChanges}
                editMode={editMode}
                pdfHeight={envPropsRef.current?.viewport?.height}
                pdfWidth={envPropsRef.current?.viewport?.width}
                handleShortcut={handleShortcut}
                focused={
                  focused.type === "weld" && focused.id === point.id
                    ? true
                    : false
                }
              />
            ))}
          {spools.length > 0 &&
            spools.map((_spool) => (
              <Spool
                spool={_spool}
                key={_spool.instanceID! as string}
                showDetails={showSpoolDetails}
                active={activeItem === _spool.instanceID}
                scale={workspacePositionRef.current.scale}
                saveChanges={saveSpoolChanges}
                editMode={editMode}
                pdfHeight={envPropsRef.current?.viewport?.height}
                pdfWidth={envPropsRef.current?.viewport?.width}
                handleShortcut={handleShortcut}
                focused={
                  focused.type === "spool" && focused.id === _spool.instanceID
                    ? true
                    : false
                }
              />
            ))}
        </WorkArea>
        <MapNavigator handleMove={shiftMap} />
      </CanvasWrapper>
    </Aligment>
  );
};

export default PdfReader;
