import {
  useImperativeHandle,
  forwardRef,
  useEffect,
  useCallback,
  useRef,
  useState,
} from "react";
import { useAnnotationCapability } from "@embedpdf/plugin-annotation/react";
import { PdfAnnotationSubtype, uuidV4 } from "@embedpdf/models";
import { useSignature } from "@app/contexts/SignatureContext";
import type {
  SignatureAPI,
  AnnotationRect,
} from "@app/components/viewer/viewerTypes";
import type { SignParameters } from "@app/hooks/tools/sign/useSignParameters";
import { useViewer } from "@app/contexts/ViewerContext";
import { useDocumentReady } from "@app/components/viewer/hooks/useDocumentReady";

/**
 * Connects the PDF signature (stamp/ink) tools to the shared ViewerContext and SignatureContext.
 */

// Minimum allowed width/height (in pixels) for a signature image or text stamp.
// This prevents rendering issues and ensures signatures are always visible and usable.
const MIN_SIGNATURE_DIMENSION = 12;

// Use 2x oversampling to improve text rendering quality (anti-aliasing) when generating signature images.
// This provides a good balance between visual fidelity and performance/memory usage.
const TEXT_OVERSAMPLE_FACTOR = 2;

const extractDataUrl = (
  value: unknown,
  depth = 0,
  visited: Set<unknown> = new Set(),
): string | undefined => {
  if (!value || depth > 6) return undefined;

  // Prevent circular references
  if (typeof value === "object" && visited.has(value)) {
    return undefined;
  }

  if (typeof value === "string") {
    return value.startsWith("data:image") ? value : undefined;
  }

  if (typeof value === "object") {
    visited.add(value);

    if (Array.isArray(value)) {
      for (const entry of value) {
        const result = extractDataUrl(entry, depth + 1, visited);
        if (result) return result;
      }
    } else {
      for (const key of Object.keys(value as Record<string, unknown>)) {
        const result = extractDataUrl(
          (value as Record<string, unknown>)[key],
          depth + 1,
          visited,
        );
        if (result) return result;
      }
    }
  }

  return undefined;
};

const createTextStampImage = (
  config: SignParameters,
  displaySize?: { width: number; height: number } | null,
): {
  dataUrl: string;
  pixelWidth: number;
  pixelHeight: number;
  displayWidth: number;
  displayHeight: number;
} | null => {
  const text = (config.signerName ?? "").trim();
  if (!text) {
    return null;
  }

  const fontSize = config.fontSize ?? 16;
  const fontFamily = config.fontFamily ?? "Helvetica";
  const textColor = config.textColor ?? "#000000";

  const paddingX = Math.max(4, Math.round(fontSize * 0.8));
  const paddingY = Math.max(4, Math.round(fontSize * 0.6));

  const measureCanvas = document.createElement("canvas");
  const measureCtx = measureCanvas.getContext("2d");
  if (!measureCtx) {
    return null;
  }

  measureCtx.font = `${fontSize}px ${fontFamily}`;
  const metrics = measureCtx.measureText(text);
  const textWidth = Math.ceil(metrics.width);
  const naturalWidth = Math.max(
    MIN_SIGNATURE_DIMENSION,
    textWidth + paddingX * 2,
  );
  const naturalHeight = Math.max(
    MIN_SIGNATURE_DIMENSION,
    Math.ceil(fontSize + paddingY * 2),
  );

  const scale =
    displaySize && naturalWidth > 0 && naturalHeight > 0
      ? Math.min(
          displaySize.width / naturalWidth,
          displaySize.height / naturalHeight,
        )
      : 1;

  const displayWidth = Math.max(MIN_SIGNATURE_DIMENSION, naturalWidth * scale);
  const displayHeight = Math.max(
    MIN_SIGNATURE_DIMENSION,
    naturalHeight * scale,
  );

  const canvasWidth = Math.max(
    MIN_SIGNATURE_DIMENSION,
    Math.round(displayWidth * TEXT_OVERSAMPLE_FACTOR),
  );
  const canvasHeight = Math.max(
    MIN_SIGNATURE_DIMENSION,
    Math.round(displayHeight * TEXT_OVERSAMPLE_FACTOR),
  );

  const canvas = document.createElement("canvas");
  canvas.width = canvasWidth;
  canvas.height = canvasHeight;

  const ctx = canvas.getContext("2d");
  if (!ctx) {
    return null;
  }

  const effectiveScale = scale * TEXT_OVERSAMPLE_FACTOR;
  ctx.scale(effectiveScale, effectiveScale);

  ctx.fillStyle = textColor;
  ctx.font = `${fontSize}px ${fontFamily}`;
  ctx.textAlign = config.textAlign || "left";
  ctx.textBaseline = "middle";

  const horizontalPadding = paddingX;
  const verticalCenter = naturalHeight / 2;

  let xPosition = horizontalPadding;
  if (config.textAlign === "center") {
    xPosition = naturalWidth / 2;
  } else if (config.textAlign === "right") {
    xPosition = naturalWidth - horizontalPadding;
  }

  ctx.fillText(text, xPosition, verticalCenter);

  return {
    dataUrl: canvas.toDataURL("image/png"),
    pixelWidth: canvasWidth,
    pixelHeight: canvasHeight,
    displayWidth,
    displayHeight,
  };
};

interface SignatureAPIBridgeProps {
  /** When true (sign tool context), the general ink/pen annotation tool is blocked. */
  isSignMode?: boolean;
}

export const SignatureAPIBridge = forwardRef<
  SignatureAPI,
  SignatureAPIBridgeProps
>(function SignatureAPIBridge({ isSignMode = false }, ref) {
  const { provides: annotationApi } = useAnnotationCapability();
  const {
    signatureConfig,
    storeImageData,
    isPlacementMode,
    placementPreviewSize,
    setSignaturesApplied,
  } = useSignature();
  const { getZoomState, registerImmediateZoomUpdate } = useViewer();
  const documentReady = useDocumentReady();
  const [currentZoom, setCurrentZoom] = useState(
    () => getZoomState()?.currentZoom ?? 1,
  );
  const lastStampImageRef = useRef<string | null>(null);

  useEffect(() => {
    setCurrentZoom(getZoomState()?.currentZoom ?? 1);
    const unregister = registerImmediateZoomUpdate((percent) => {
      setCurrentZoom(Math.max(percent / 100, 0.01));
    });
    return () => {
      unregister?.();
    };
  }, [getZoomState, registerImmediateZoomUpdate]);

  // When entering sign mode, deactivate any active annotation tool immediately.
  // Only signature-specific tools (signatureInk, stamp) should be usable.
  useEffect(() => {
    if (isSignMode && annotationApi && documentReady) {
      annotationApi.setActiveTool(null);
    }
  }, [isSignMode, annotationApi, documentReady]);

  const cssToPdfSize = useCallback(
    (size: { width: number; height: number }) => {
      const zoom = currentZoom || 1;
      const factor = 1 / zoom;
      return {
        width: size.width * factor,
        height: size.height * factor,
      };
    },
    [currentZoom],
  );

  const applyStampDefaults = useCallback(
    (
      imageSrc: string,
      subject: string,
      size?: { width: number; height: number },
    ) => {
      if (!annotationApi) return;

      annotationApi.setActiveTool(null);
      annotationApi.setActiveTool("stamp");
      const stampTool = annotationApi.getActiveTool();
      if (stampTool && stampTool.id === "stamp") {
        annotationApi.setToolDefaults("stamp", {
          imageSrc,
          subject,
          ...(size
            ? { imageSize: { width: size.width, height: size.height } }
            : {}),
        });
      }
    },
    [annotationApi],
  );

  const configureStampDefaults = useCallback(async () => {
    if (!annotationApi || !signatureConfig) {
      return;
    }

    try {
      if (
        signatureConfig.signatureType === "text" &&
        signatureConfig.signerName
      ) {
        const textStamp = createTextStampImage(
          signatureConfig,
          placementPreviewSize,
        );
        if (textStamp) {
          const displaySize = placementPreviewSize ?? {
            width: textStamp.displayWidth,
            height: textStamp.displayHeight,
          };
          const pdfSize = cssToPdfSize(displaySize);
          lastStampImageRef.current = textStamp.dataUrl;
          applyStampDefaults(
            textStamp.dataUrl,
            `Text Signature - ${signatureConfig.signerName}`,
            pdfSize,
          );
        }
        return;
      }

      if (signatureConfig.signatureData) {
        const pdfSize = placementPreviewSize
          ? cssToPdfSize(placementPreviewSize)
          : undefined;
        lastStampImageRef.current = signatureConfig.signatureData;
        applyStampDefaults(
          signatureConfig.signatureData,
          `Digital Signature - ${signatureConfig.reason || "Document signing"}`,
          pdfSize,
        );
        return;
      }
    } catch (error) {
      console.error("Error preparing signature defaults:", error);
    }
  }, [
    annotationApi,
    signatureConfig,
    placementPreviewSize,
    applyStampDefaults,
    cssToPdfSize,
  ]);

  // Enable keyboard deletion of selected annotations
  useEffect(() => {
    // Always enable delete key when we have annotation API and are in sign mode
    if (!annotationApi || isPlacementMode === undefined || !documentReady)
      return;

    const handleKeyDown = (event: KeyboardEvent) => {
      // Skip delete/backspace while a text input/textarea is focused (e.g., editing textbox)
      const target = event.target as HTMLElement | null;
      const tag = target?.tagName?.toLowerCase();
      const editable = target?.getAttribute?.("contenteditable");
      if (tag === "input" || tag === "textarea" || editable === "true") {
        return;
      }

      if (event.key === "Delete" || event.key === "Backspace") {
        const selectedAnnotation = annotationApi.getSelectedAnnotation?.();

        if (selectedAnnotation) {
          const annotation = selectedAnnotation as any;
          const pageIndex = annotation.object?.pageIndex || 0;
          const id = annotation.object?.id;

          // For STAMP annotations, ensure image data is preserved before deletion
          if (annotation.object?.type === 13 && id) {
            // Get current annotation data to ensure we have latest image data stored
            const pageAnnotationsTask = annotationApi.getPageAnnotations?.({
              pageIndex,
            });
            if (pageAnnotationsTask) {
              pageAnnotationsTask
                .toPromise()
                .then((pageAnnotations: any) => {
                  const currentAnn = pageAnnotations?.find(
                    (ann: any) => ann.id === id,
                  );
                  if (currentAnn && currentAnn.imageSrc) {
                    // Ensure the image data is stored in our persistent store
                    storeImageData(id, currentAnn.imageSrc);
                  }
                })
                .catch(console.error);
            }
          }

          // Use EmbedPDF's native deletion which should integrate with history
          if ((annotationApi as any).deleteSelected) {
            (annotationApi as any).deleteSelected();
          } else {
            // Fallback to direct deletion - less ideal for history
            if (id) {
              annotationApi.deleteAnnotation(pageIndex, id);
            }
          }
        }
      }
    };

    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [annotationApi, storeImageData, isPlacementMode]);

  useImperativeHandle(
    ref,
    () => ({
      addImageSignature: (
        signatureData: string,
        x: number,
        y: number,
        width: number,
        height: number,
        pageIndex: number,
      ) => {
        if (!annotationApi) return;

        // Create image stamp annotation with proper image data

        const annotationId = uuidV4();

        // Store image data in our persistent store
        storeImageData(annotationId, signatureData);

        annotationApi.createAnnotation(pageIndex, {
          type: PdfAnnotationSubtype.STAMP,
          rect: {
            origin: { x, y },
            size: { width, height },
          },
          author: "Digital Signature",
          subject: "Digital Signature",
          pageIndex: pageIndex,
          id: annotationId,
          created: new Date(),
          // Store image data in multiple places to ensure history captures it
          imageSrc: signatureData,
          contents: signatureData, // Some annotation systems use contents
          data: signatureData, // Try data field
          imageData: signatureData, // Try imageData field
          appearance: signatureData, // Try appearance field
        });
      },

      activateDrawMode: () => {
        if (!annotationApi) return;

        // Use signatureInk (not ink) so the annotation pen tool stays unaffected
        annotationApi.setActiveTool("signatureInk");

        const activeTool = annotationApi.getActiveTool();
        if (activeTool && activeTool.id === "signatureInk") {
          annotationApi.setToolDefaults("signatureInk", {
            color: "#000000",
            thickness: 2,
            lineWidth: 2,
            strokeWidth: 2,
            width: 2,
          });
        }
      },

      activateSignaturePlacementMode: () => {
        if (!annotationApi || !signatureConfig) return;

        configureStampDefaults().catch((error) => {
          console.error("Error activating signature tool:", error);
        });
      },

      updateDrawSettings: (color: string, size: number) => {
        if (!annotationApi) return;

        // Always update ink tool defaults - use multiple property names for compatibility
        annotationApi.setToolDefaults("ink", {
          color: color,
          thickness: size,
          lineWidth: size,
          strokeWidth: size,
          width: size,
        });

        // Only reactivate ink if it was already the active tool — never auto-activate it.
        // This prevents the ink (pen annotation) tool from being activated when
        // updateDrawSettings is called on sign-tool mount.
        const currentTool = annotationApi.getActiveTool();
        if (currentTool?.id === "ink") {
          annotationApi.setActiveTool(null);
          setTimeout(() => {
            annotationApi.setActiveTool("ink");
          }, 50);
        }
      },

      activateDeleteMode: () => {
        if (!annotationApi) return;
        // Activate selection tool to allow selecting and deleting annotations
        // Users can click annotations to select them, then press Delete key or right-click to delete
        annotationApi.setActiveTool("select");
      },

      deleteAnnotation: (annotationId: string, pageIndex: number) => {
        if (!annotationApi) return;

        // Before deleting, try to preserve image data for potential undo
        const pageAnnotationsTask = annotationApi.getPageAnnotations?.({
          pageIndex,
        });
        if (pageAnnotationsTask) {
          pageAnnotationsTask
            .toPromise()
            .then((pageAnnotations: any) => {
              const annotation = pageAnnotations?.find(
                (ann: any) => ann.id === annotationId,
              );
              if (
                annotation &&
                annotation.type === PdfAnnotationSubtype.STAMP &&
                annotation.imageSrc
              ) {
                // Store image data before deletion
                storeImageData(annotationId, annotation.imageSrc);
              }
            })
            .catch(console.error);
        }

        // Delete specific annotation by ID
        annotationApi.deleteAnnotation(pageIndex, annotationId);
      },

      deactivateTools: () => {
        if (!annotationApi) return;
        annotationApi.setActiveTool(null);
      },

      getPageAnnotations: async (pageIndex: number): Promise<any[]> => {
        if (!annotationApi || !annotationApi.getPageAnnotations) {
          console.warn("getPageAnnotations not available");
          return [];
        }

        try {
          const pageAnnotationsTask = annotationApi.getPageAnnotations({
            pageIndex,
          });
          if (pageAnnotationsTask && pageAnnotationsTask.toPromise) {
            const annotations = await pageAnnotationsTask.toPromise();
            return annotations || [];
          }
          return [];
        } catch (error) {
          console.error(
            `Error getting annotations for page ${pageIndex}:`,
            error,
          );
          return [];
        }
      },

      moveAnnotation: (
        pageIndex: number,
        annotationId: string,
        newRect: AnnotationRect,
      ) => {
        if (!annotationApi) return;
        // v2.7.0: move signature stamp to newRect without regenerating the AP stream,
        // preserving the original appearance (image data stays intact).
        (annotationApi as any).moveAnnotation?.(
          pageIndex,
          annotationId,
          newRect,
        );
      },
    }),
    [annotationApi, signatureConfig, placementPreviewSize, applyStampDefaults],
  );

  useEffect(() => {
    if (!annotationApi?.onAnnotationEvent || !documentReady) {
      return;
    }

    const unsubscribe = annotationApi.onAnnotationEvent((event) => {
      if (event.type !== "create" && event.type !== "update") {
        return;
      }

      const annotation: any = event.annotation;
      const annotationId: string | undefined = annotation?.id;
      if (!annotationId) {
        return;
      }

      // Mark signatures as not applied when a new signature is placed
      if (event.type === "create") {
        setSignaturesApplied(false);
      }

      const directData =
        extractDataUrl(annotation.imageSrc) ||
        extractDataUrl(annotation.imageData) ||
        extractDataUrl(annotation.appearance) ||
        extractDataUrl(annotation.stampData) ||
        extractDataUrl(annotation.contents) ||
        extractDataUrl(annotation.data) ||
        extractDataUrl(annotation.customData) ||
        extractDataUrl(annotation.asset);

      const dataToStore = directData || lastStampImageRef.current;
      if (dataToStore) {
        storeImageData(annotationId, dataToStore);
      }
    });

    return () => {
      unsubscribe?.();
    };
  }, [annotationApi, storeImageData, setSignaturesApplied, documentReady]);

  useEffect(() => {
    if (!isPlacementMode || !documentReady) {
      return;
    }

    let cancelled = false;
    configureStampDefaults().catch((error) => {
      if (!cancelled) {
        console.error("Error updating signature defaults:", error);
      }
    });

    return () => {
      cancelled = true;
    };
  }, [
    isPlacementMode,
    configureStampDefaults,
    placementPreviewSize,
    signatureConfig,
    documentReady,
  ]);

  return null; // This is a bridge component with no UI
});

SignatureAPIBridge.displayName = "SignatureAPIBridge";
