"use client";

import { useEffect, useRef, useState } from "react";
import { LoadingThreeDot } from "./loading-three-dot";

const IDE_SCHEMES = [
  {
    matches: ["antigravity"],
    createUrl: (locator: string) => `antigravity://file/${locator}`,
  },
  {
    matches: ["cursor"],
    createUrl: (locator: string) => `cursor://file/${locator}`,
  },
  {
    matches: ["vscode", "code"],
    createUrl: (locator: string) => `vscode://file/${locator}`,
  },
  {
    matches: ["webstorm", "intellij"],
    createUrl: (locator: string) => `webstorm://open?file=${locator}`,
  },
  {
    matches: ["sublime"],
    createUrl: (locator: string) => `subl://open?url=file://${locator}`,
  },
  {
    matches: ["atom", "nova"],
    createUrl: (locator: string) => `atom://open?url=file://${locator}`,
  },
] as const;

function parseLocator(locator: string) {
  const match = locator.match(/^(.*):(\d+|unknown):(\d+|unknown)$/);

  if (!match) {
    return { filePath: locator, line: null, column: null };
  }

  const [, filePath, line, column] = match;

  return {
    filePath,
    line: line === "unknown" ? null : Number(line),
    column: column === "unknown" ? null : Number(column),
  };
}

export function DevClickToComponent() {
  const [isInspecting, setIsInspecting] = useState(false);
  const [isMobile, setIsMobile] = useState(false);
  const [position, setPosition] = useState<{ x: number; y: number } | null>(
    null,
  );
  const [isDragging, setIsDragging] = useState(false);

  const dragStart = useRef({
    x: 0,
    y: 0,
    buttonX: 0,
    buttonY: 0,
    hasMoved: false,
  });

  // Detect mobile size
  useEffect(() => {
    const checkSize = () => {
      setIsMobile(window.innerWidth <= 768);
    };
    checkSize();
    window.addEventListener("resize", checkSize);
    return () => window.removeEventListener("resize", checkSize);
  }, []);

  // Initialize button position
  useEffect(() => {
    if (typeof window !== "undefined") {
      setPosition({
        x: window.innerWidth - 66, // 50px width + 16px margin
        y: window.innerHeight - 66, // 50px height + 16px margin
      });
    }
  }, [isMobile]);

  // Keep button in bounds on resize
  useEffect(() => {
    const handleResize = () => {
      if (!isMobile) return;
      setPosition((prev) => {
        if (!prev) return null;
        return {
          x: Math.max(16, Math.min(window.innerWidth - 66, prev.x)),
          y: Math.max(16, Math.min(window.innerHeight - 66, prev.y)),
        };
      });
    };
    window.addEventListener("resize", handleResize);
    return () => window.removeEventListener("resize", handleResize);
  }, [isMobile]);

  useEffect(() => {
    const userAgent = navigator.userAgent.toLowerCase();

    // Desktop: Alt + Click logic
    const handleDesktopClick = (event: MouseEvent) => {
      if (!event.altKey) {
        return;
      }

      const target = event.target;
      if (!(target instanceof HTMLElement)) {
        return;
      }

      const locator = target
        .closest<HTMLElement>("[data-locator]")
        ?.getAttribute("data-locator");

      if (!locator) {
        return;
      }

      event.preventDefault();
      event.stopPropagation();

      const { filePath, line, column } = parseLocator(locator);
      const positionSuffix =
        line === null ? "" : `:${line}${column === null ? "" : `:${column}`}`;

      const locatorString = `${filePath}${positionSuffix}`;

      // Use the API route to execute the shell command which reuses the existing window
      fetch("/api/open-in-ide", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ locator: locatorString, userAgent }),
      }).catch((err) => {
        console.error("[DevClickToComponent] Error sending API request:", err);

        // Fallback to URL scheme
        const ideUrl =
          IDE_SCHEMES.find(({ matches }) =>
            matches.some((match) => userAgent.includes(match)),
          )?.createUrl(locatorString) ?? `antigravity://file/${locatorString}`;

        try {
          window.location.href = ideUrl;
        } catch {
          window.open(`file://${filePath}`, "_blank", "noopener,noreferrer");
        }
      });
    };

    document.addEventListener("click", handleDesktopClick, true);
    return () => {
      document.removeEventListener("click", handleDesktopClick, true);
    };
  }, []);

  // Mobile: Inspect Mode click interception
  useEffect(() => {
    if (!isMobile || !isInspecting) return;

    const userAgent = navigator.userAgent.toLowerCase();

    const handleInspectClick = async (event: MouseEvent) => {
      const target = event.target;
      if (!(target instanceof Element)) return;

      // Ignore clicks on the inspect button itself
      if (target.closest(".dev-inspect-btn")) {
        return;
      }

      event.preventDefault();
      event.stopPropagation();

      const locator = target
        .closest("[data-locator]")
        ?.getAttribute("data-locator");

      if (locator) {
        console.log(
          `[DevClickToComponent] Inspect Mode matched element. Opening in IDE:`,
          locator,
        );

        // Flash target element outline briefly as visual feedback
        const element = target.closest<HTMLElement>("[data-locator]");
        if (element) {
          const originalOutline = element.style.outline;
          element.style.outline = "3px solid #3b82f6";
          setTimeout(() => {
            element.style.outline = originalOutline;
          }, 400);
        }

        try {
          await fetch("/api/open-in-ide", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({ locator, userAgent }),
          });
        } catch (err) {
          console.error(
            "[DevClickToComponent] Error sending API request:",
            err,
          );
        }
      }

      setIsInspecting(false);
    };

    // Use capturing phase to intercept clicks/taps before they trigger other actions
    document.addEventListener("click", handleInspectClick, true);

    return () => {
      document.removeEventListener("click", handleInspectClick, true);
    };
  }, [isMobile, isInspecting]);

  // Dev mode: Intercept and forward console logs globally
  useEffect(() => {
    const originalLog = console.log;
    const originalWarn = console.warn;
    const originalError = console.error;

    let isSending = false;

    const forwardLog = async (type: "log" | "warn" | "error", args: any[]) => {
      if (isSending) return;
      isSending = true;

      try {
        await fetch("/api/remote-logs", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ type, args }),
        });
      } catch (err) {
        originalError.call(
          console,
          "[DevClickToComponent] Failed to forward log to server:",
          err,
        );
      } finally {
        isSending = false;
      }
    };

    console.log = (...args: any[]) => {
      originalLog.apply(console, args);
      forwardLog("log", args);
    };

    console.warn = (...args: any[]) => {
      originalWarn.apply(console, args);
      forwardLog("warn", args);
    };

    console.error = (...args: any[]) => {
      originalError.apply(console, args);
      forwardLog("error", args);
    };

    return () => {
      console.log = originalLog;
      console.warn = originalWarn;
      console.error = originalError;
    };
  }, []);

  // Dev mode: Intercept and forward fetch requests globally
  useEffect(() => {
    const originalFetch = window.fetch;

    window.fetch = async (input, init) => {
      const url =
        typeof input === "string"
          ? input
          : input instanceof URL
            ? input.href
            : input instanceof Request
              ? input.url
              : "";

      // Ignore logging requests to prevent infinite recursion
      if (
        url.includes("/api/remote-logs") ||
        url.includes("/api/remote-network") ||
        url.includes("/api/open-in-ide")
      ) {
        return originalFetch(input, init);
      }

      const method =
        init?.method ?? (input instanceof Request ? input.method : "GET");
      const startTime = Date.now();

      let requestBody: string | null = null;
      if (init?.body) {
        if (typeof init.body === "string") {
          requestBody = init.body;
        } else {
          requestBody = "[Non-string payload]";
        }
      } else if (input instanceof Request) {
        try {
          const clonedReq = input.clone();
          requestBody = await clonedReq.text();
        } catch {
          requestBody = null;
        }
      }

      try {
        const response = await originalFetch(input, init);
        const duration = Date.now() - startTime;
        const status = response.status;

        let responseBody: string | null = null;
        try {
          const clonedRes = response.clone();
          responseBody = await clonedRes.text();
        } catch {
          responseBody = "[Unreadable response body]";
        }

        originalFetch("/api/remote-network", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            method,
            url,
            status,
            requestBody,
            responseBody,
            duration,
          }),
        }).catch(() => {});

        return response;
      } catch (err) {
        const duration = Date.now() - startTime;

        originalFetch("/api/remote-network", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            method,
            url,
            status: 0,
            requestBody,
            responseBody: String(err),
            duration,
          }),
        }).catch(() => {});

        throw err;
      }
    };

    return () => {
      window.fetch = originalFetch;
    };
  }, []);

  // Dev mode: Intercept and forward XMLHttpRequest (XHR) requests globally
  useEffect(() => {
    const originalOpen = XMLHttpRequest.prototype.open;
    const originalSend = XMLHttpRequest.prototype.send;

    XMLHttpRequest.prototype.open = function (
      this: any,
      method: any,
      url: any,
      ...args: any[]
    ) {
      this._method = method;
      this._url = typeof url === "string" ? url : url.toString();
      return originalOpen.apply(this, [method, url, ...args] as any);
    };

    XMLHttpRequest.prototype.send = function (this: any, body?: any) {
      const url = this._url || "";
      const method = this._method || "GET";
      const startTime = Date.now();

      // Avoid infinite recursion on dev logging endpoints
      if (
        url.includes("/api/remote-logs") ||
        url.includes("/api/remote-network") ||
        url.includes("/api/open-in-ide")
      ) {
        return originalSend.apply(this, [body]);
      }

      this.addEventListener("loadend", () => {
        const duration = Date.now() - startTime;
        const status = this.status;
        let responseBody = "";

        try {
          responseBody = this.responseText;
        } catch {
          try {
            if (typeof this.response === "string") {
              responseBody = this.response;
            } else if (this.response) {
              responseBody = JSON.stringify(this.response);
            }
          } catch {
            responseBody = "[Binary or unreadable response]";
          }
        }

        let requestBody: string | null = null;
        if (body) {
          if (typeof body === "string") {
            requestBody = body;
          } else {
            requestBody = "[Non-string payload]";
          }
        }

        window
          .fetch("/api/remote-network", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              method,
              url,
              status,
              requestBody,
              responseBody,
              duration,
            }),
          })
          .catch(() => {});
      });

      return originalSend.apply(this, [body]);
    };

    return () => {
      XMLHttpRequest.prototype.open = originalOpen;
      XMLHttpRequest.prototype.send = originalSend;
    };
  }, []);

  // Dragging event handlers
  const handlePointerDown = (e: React.PointerEvent<HTMLButtonElement>) => {
    e.currentTarget.setPointerCapture(e.pointerId);
    setIsDragging(true);
    const startX = position?.x ?? window.innerWidth - 66;
    const startY = position?.y ?? window.innerHeight - 126;
    dragStart.current = {
      x: e.clientX,
      y: e.clientY,
      buttonX: startX,
      buttonY: startY,
      hasMoved: false,
    };
  };

  const handlePointerMove = (e: React.PointerEvent<HTMLButtonElement>) => {
    if (!isDragging) return;
    const dx = e.clientX - dragStart.current.x;
    const dy = e.clientY - dragStart.current.y;

    if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
      dragStart.current.hasMoved = true;
    }

    const newX = Math.max(
      16,
      Math.min(window.innerWidth - 66, dragStart.current.buttonX + dx),
    );
    const newY = Math.max(
      16,
      Math.min(window.innerHeight - 126, dragStart.current.buttonY + dy),
    );

    setPosition({ x: newX, y: newY });
  };

  const handlePointerUp = (e: React.PointerEvent<HTMLButtonElement>) => {
    if (!isDragging) return;
    e.currentTarget.releasePointerCapture(e.pointerId);
    setIsDragging(false);
  };

  const handleInspectClick = () => {
    if (!dragStart.current.hasMoved) {
      setIsInspecting(!isInspecting);
    }
  };

  const [isSendingStorage, setIsSendingStorage] = useState(false);
  const [sendSuccess, setSendSuccess] = useState(false);

  const handleSendStorageClick = async () => {
    if (dragStart.current.hasMoved) return;
    if (isSendingStorage) return;

    setIsSendingStorage(true);
    setSendSuccess(false);

    try {
      const data: Record<string, any> = {};
      for (let i = 0; i < window.localStorage.length; i++) {
        const key = window.localStorage.key(i);
        if (key) {
          const val = window.localStorage.getItem(key);
          try {
            data[key] = JSON.parse(val || "");
          } catch {
            data[key] = val;
          }
        }
      }

      const response = await fetch("/api/dev-storage", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
      });

      if (response.ok) {
        setSendSuccess(true);
        setTimeout(() => setSendSuccess(false), 2000);
      }
    } catch (err) {
      console.error(
        "[DevClickToComponent] Failed to send localStorage values:",
        err,
      );
    } finally {
      setIsSendingStorage(false);
    }
  };

  return (
    <div
      style={{
        position: "fixed",
        left: position ? `${position.x}px` : "auto",
        top: position ? `${position.y}px` : "auto",
        bottom: position ? "auto" : "16px",
        right: position ? "auto" : "16px",
        display: "flex",
        flexDirection: "column",
        gap: "10px",
        zIndex: 99998,
        touchAction: "none",
      }}
    >
      {/* Send LocalStorage Button */}
      <button
        onPointerDown={handlePointerDown}
        onPointerMove={handlePointerMove}
        onPointerUp={handlePointerUp}
        onClick={handleSendStorageClick}
        style={{
          width: "50px",
          height: "50px",
          borderRadius: "25px",
          backgroundColor: sendSuccess ? "#10b981" : "#a855f7",
          color: "#ffffff",
          border: "none",
          boxShadow: sendSuccess
            ? "0 0 15px rgba(16, 185, 129, 0.6)"
            : "0 4px 12px rgba(0,0,0,0.35)",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          cursor: isDragging ? "grabbing" : "pointer",
          transition: isDragging
            ? "none"
            : "background-color 0.25s, transform 0.25s, box-shadow 0.25s",
          outline: "none",
          transform: isSendingStorage ? "scale(0.95)" : "scale(1)",
        }}
        title="Send LocalStorage to Terminal API"
      >
        {isSendingStorage ? (
          <LoadingThreeDot className="scale-75" />
        ) : sendSuccess ? (
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={2.5}
            stroke="currentColor"
            style={{ width: "22px", height: "22px", pointerEvents: "none" }}
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="m4.5 12.75 6 6 9-13.5"
            />
          </svg>
        ) : (
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            strokeWidth={2}
            stroke="currentColor"
            style={{ width: "22px", height: "22px", pointerEvents: "none" }}
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M12 16.5V9.75m0 0 3 3m-3-3-3 3M6.75 19.5a4.5 4.5 0 0 1-1.41-8.775 5.25 5.25 0 0 1 10.233-2.33 3 3 0 0 1 3.758 3.848A3.752 3.752 0 0 1 18 19.5H6.75Z"
            />
          </svg>
        )}
      </button>

      {/* Floating Action Draggable Button (Inspect Mode) */}
      {isMobile && (
        <button
          className="dev-inspect-btn"
          onPointerDown={handlePointerDown}
          onPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onClick={handleInspectClick}
          style={{
            width: "50px",
            height: "50px",
            borderRadius: "25px",
            backgroundColor: isInspecting ? "#3b82f6" : "#1f2937",
            color: "#ffffff",
            border: "none",
            boxShadow: isInspecting
              ? "0 0 15px rgba(59, 130, 246, 0.6)"
              : "0 4px 12px rgba(0,0,0,0.35)",
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
            cursor: isDragging ? "grabbing" : "pointer",
            transition: isDragging
              ? "none"
              : "background-color 0.25s, transform 0.25s, box-shadow 0.25s",
            outline: "none",
            transform: isInspecting ? "scale(1.1)" : "scale(1)",
          }}
          title="Toggle Dev Inspect Mode (Drag to move)"
        >
          {isInspecting ? (
            <svg
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
              strokeWidth={2.5}
              stroke="currentColor"
              style={{ width: "22px", height: "22px", pointerEvents: "none" }}
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                d="M6 18 18 6M6 6l12 12"
              />
            </svg>
          ) : (
            <svg
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
              strokeWidth={2}
              stroke="currentColor"
              style={{ width: "22px", height: "22px", pointerEvents: "none" }}
            >
              <path
                strokeLinecap="round"
                strokeLinejoin="round"
                d="m15.75 15.75-2.489-2.489m0 0a3.375 3.375 0 1 0-4.773-4.773 3.375 3.375 0 0 0 4.774 4.774ZM21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
              />
            </svg>
          )}
        </button>
      )}
    </div>
  );
}

export default DevClickToComponent;
