"use client";

/**
 * PostHog Analytics Provider
 *
 * Initializes PostHog on the client side and provides comprehensive user action tracking:
 * - Page view tracking
 * - Global click tracking (buttons, links, interactive elements)
 * - Scroll tracking (throttled)
 * - Form interaction tracking (focus, input, submit)
 * - Visibility change tracking (tab switching)
 *
 * All actions are recorded to the error context ring buffer for debugging.
 */

import { useEffect, useCallback, useRef, useMemo } from "react";
import { usePathname, useSearchParams } from "next/navigation";
import { initPostHog, trackPageView } from "@/lib/posthog-tracker";
import { trackClick, trackFormSubmit, trackAction } from "@/lib/action-tracker";
import { recordAction } from "@/lib/error-context";

// Throttle helper for events
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function throttle<T extends (...args: any[]) => void>(
  func: T,
  limit: number,
): T {
  let inThrottle = false;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return ((...args: any[]) => {
    if (!inThrottle) {
      func(...args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  }) as T;
}

// Get element identifier for tracking
function getElementIdentifier(element: HTMLElement): string {
  // Priority: data-track-name > id > aria-label > text content > tag
  if (element.dataset.trackName) return element.dataset.trackName;
  if (element.id) return `#${element.id}`;
  if (element.getAttribute("aria-label"))
    return element.getAttribute("aria-label")!;

  // For buttons/links, use text content (truncated)
  const text = element.textContent?.trim().slice(0, 30);
  if (text) return text;

  // Fallback to tag + class
  const tag = element.tagName.toLowerCase();
  const className = element.className?.split?.(" ")?.[0];
  return className ? `${tag}.${className}` : tag;
}

// Get element location context
function getElementLocation(element: HTMLElement): string | undefined {
  if (element.dataset.trackLocation) return element.dataset.trackLocation;

  // Try to find a parent section/region
  const section = element.closest(
    'section, header, footer, main, aside, nav, [role="region"]',
  );
  if (section) {
    const id = section.id || section.getAttribute("aria-label");
    if (id) return id;
  }

  return undefined;
}

// Store original fetch for interception
const originalFetch = typeof window !== "undefined" ? window.fetch : null;
let fetchIntercepted = false;

export function PostHogProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const lastScrollPosition = useRef(0);
  const scrollDirection = useRef<"up" | "down">("down");

  // ============================================================================
  // API Request Interceptor - Tracks all fetch calls
  // ============================================================================
  useEffect(() => {
    if (typeof window === "undefined" || !originalFetch || fetchIntercepted)
      return;

    fetchIntercepted = true;

    window.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
      const startTime = Date.now();
      const url =
        typeof input === "string"
          ? input
          : input instanceof URL
            ? input.href
            : input.url;
      const method = init?.method || "GET";

      // Only track API calls (not static assets)
      const isApiCall =
        url.includes("/api/") ||
        url.includes("/graphql") ||
        (url.startsWith("http") &&
          !url.match(/\.(js|css|png|jpg|jpeg|gif|svg|woff|woff2|ttf|ico)$/i));

      if (!isApiCall) {
        return originalFetch(input, init);
      }

      // Parse URL for cleaner logging
      let apiPath = url;
      try {
        const urlObj = new URL(url, window.location.origin);
        apiPath =
          urlObj.pathname +
          (urlObj.search ? "?" + urlObj.search.slice(0, 50) : "");
      } catch {
        // Keep original URL if parsing fails
      }

      // Record the API request start
      recordAction(`api:${method}:${apiPath}`, {
        component: "fetch",
        data: { method, url: apiPath },
      });

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

        // Record the API response
        recordAction(`api_response:${response.status}:${apiPath}`, {
          component: "fetch",
          data: {
            method,
            url: apiPath,
            status: response.status,
            ok: response.ok,
            duration_ms: duration,
          },
        });

        // Track failed API calls more prominently
        if (!response.ok) {
          recordAction(`api_error:${response.status}:${apiPath}`, {
            component: "fetch",
            data: {
              method,
              url: apiPath,
              status: response.status,
              statusText: response.statusText,
              duration_ms: duration,
            },
          });
        }

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

        // Record network errors
        recordAction(`api_network_error:${apiPath}`, {
          component: "fetch",
          data: {
            method,
            url: apiPath,
            error: error instanceof Error ? error.message : "Unknown error",
            duration_ms: duration,
          },
        });

        throw error;
      }
    };

    // Cleanup: restore original fetch on unmount
    return () => {
      if (originalFetch) {
        window.fetch = originalFetch;
        fetchIntercepted = false;
      }
    };
  }, []);

  // ============================================================================
  // Global Click Handler - Tracks ALL clicks with context
  // ============================================================================
  const handleGlobalClick = useCallback((event: MouseEvent) => {
    const target = event.target as HTMLElement;
    if (!target) return;

    // Find the closest interactive element
    const interactiveElement = target.closest(
      'button, a, [role="button"], input[type="submit"], input[type="button"], [onclick], [data-track-name]',
    ) as HTMLElement;

    if (interactiveElement) {
      // Check if already handled by TrackedButton
      if (interactiveElement.dataset.trackHandled === "true") {
        delete interactiveElement.dataset.trackHandled;
        return;
      }

      const name = getElementIdentifier(interactiveElement);
      const location = getElementLocation(interactiveElement);

      trackClick(name, location, {
        tag: interactiveElement.tagName.toLowerCase(),
        href: (interactiveElement as HTMLAnchorElement).href || undefined,
      });
    } else {
      // Track non-interactive clicks for context (simplified)
      const name = getElementIdentifier(target);
      recordAction(`click:${name}`, {
        location: getElementLocation(target),
      });
    }
  }, []);

  // ============================================================================
  // Scroll Handler - Tracks scroll milestones
  // ============================================================================
  const handleScroll = useMemo(
    () =>
      throttle(() => {
        const scrollTop = window.scrollY;
        const docHeight =
          document.documentElement.scrollHeight - window.innerHeight;
        const scrollPercent = Math.round((scrollTop / docHeight) * 100);

        // Determine scroll direction
        const newDirection =
          scrollTop > lastScrollPosition.current ? "down" : "up";
        lastScrollPosition.current = scrollTop;

        // Only record at meaningful milestones (25%, 50%, 75%, 90%, 100%)
        const milestones = [25, 50, 75, 90, 100];
        const milestone = milestones.find(
          (m) => scrollPercent >= m && scrollPercent < m + 10,
        );

        if (milestone && scrollDirection.current !== newDirection) {
          scrollDirection.current = newDirection;
          recordAction(`scroll:${milestone}%`, {
            data: { direction: newDirection, percent: scrollPercent },
          });
        }

        // Always track direction changes for error context
        if (newDirection !== scrollDirection.current) {
          scrollDirection.current = newDirection;
        }
      }, 500), // Throttle to max once per 500ms
    [],
  );

  // ============================================================================
  // Form Interaction Handler - Tracks form field focus and submissions
  // ============================================================================
  const handleFocusIn = useCallback((event: FocusEvent) => {
    const target = event.target as HTMLElement;
    if (!target) return;

    // Track form field focus
    if (
      target.tagName === "INPUT" ||
      target.tagName === "TEXTAREA" ||
      target.tagName === "SELECT"
    ) {
      const input = target as HTMLInputElement;
      const fieldName =
        input.name || input.id || input.placeholder || input.type;
      const form = input.closest("form");
      const formName =
        form?.id ||
        form?.name ||
        form?.getAttribute("aria-label") ||
        "unknown_form";

      recordAction(`focus:${formName}/${fieldName}`, {
        component: formName,
        data: { field: fieldName, type: input.type },
      });
    }
  }, []);

  const handleFormSubmit = useCallback((event: Event) => {
    const form = event.target as HTMLFormElement;
    if (!form || form.tagName !== "FORM") return;

    const formName =
      form.id || form.name || form.getAttribute("aria-label") || "unknown_form";
    const location = getElementLocation(form);

    trackFormSubmit(formName, location, {
      action: form.action,
      method: form.method,
    });
  }, []);

  // ============================================================================
  // Input Change Handler - Tracks when users type/change values
  // ============================================================================
  const handleInput = useMemo(
    () =>
      throttle((event: Event) => {
        const target = event.target as HTMLInputElement;
        if (!target) return;

        // Only track that input happened, not the value (privacy)
        if (
          target.tagName === "INPUT" ||
          target.tagName === "TEXTAREA" ||
          target.tagName === "SELECT"
        ) {
          const fieldName = target.name || target.id || target.type;
          const form = target.closest("form");
          const formName = form?.id || form?.name || "unknown_form";

          recordAction(`input:${formName}/${fieldName}`, {
            component: formName,
            data: { field: fieldName },
          });
        }
      }, 1000), // Throttle to max once per second per field
    [],
  );

  // ============================================================================
  // Visibility Change Handler - Tracks tab switching
  // ============================================================================
  const handleVisibilityChange = useCallback(() => {
    const state = document.visibilityState;
    recordAction(`visibility:${state}`, {
      data: { hidden: document.hidden },
    });

    if (state === "visible") {
      trackAction("visibility", "tab_returned", pathname || undefined);
    }
  }, [pathname]);

  // ============================================================================
  // Error Handler - Tracks unhandled errors
  // ============================================================================
  const handleError = useCallback((event: ErrorEvent) => {
    recordAction(`error:unhandled`, {
      data: {
        message: event.message,
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno,
      },
    });
  }, []);

  const handleUnhandledRejection = useCallback(
    (event: PromiseRejectionEvent) => {
      recordAction(`error:promise_rejection`, {
        data: {
          reason: String(event.reason),
        },
      });
    },
    [],
  );

  // ============================================================================
  // Setup all event listeners
  // ============================================================================
  useEffect(() => {
    // Initialize PostHog on mount
    initPostHog();

    // Click tracking
    document.addEventListener("click", handleGlobalClick, {
      passive: true,
      capture: true,
    });

    // Scroll tracking
    window.addEventListener("scroll", handleScroll, { passive: true });

    // Form tracking
    document.addEventListener("focusin", handleFocusIn, { passive: true });
    document.addEventListener("submit", handleFormSubmit, { capture: true });
    document.addEventListener("input", handleInput, { passive: true });

    // Visibility tracking
    document.addEventListener("visibilitychange", handleVisibilityChange);

    // Error tracking
    window.addEventListener("error", handleError);
    window.addEventListener("unhandledrejection", handleUnhandledRejection);

    return () => {
      document.removeEventListener("click", handleGlobalClick, {
        capture: true,
      });
      window.removeEventListener("scroll", handleScroll);
      document.removeEventListener("focusin", handleFocusIn);
      document.removeEventListener("submit", handleFormSubmit, {
        capture: true,
      });
      document.removeEventListener("input", handleInput);
      document.removeEventListener("visibilitychange", handleVisibilityChange);
      window.removeEventListener("error", handleError);
      window.removeEventListener(
        "unhandledrejection",
        handleUnhandledRejection,
      );
    };
  }, [
    handleGlobalClick,
    handleScroll,
    handleFocusIn,
    handleFormSubmit,
    handleInput,
    handleVisibilityChange,
    handleError,
    handleUnhandledRejection,
  ]);

  // ============================================================================
  // Page view tracking on route changes
  // ============================================================================
  useEffect(() => {
    if (pathname) {
      const url =
        pathname +
        (searchParams?.toString() ? `?${searchParams.toString()}` : "");
      trackPageView(url, {
        referrer: document.referrer,
      });

      // Also record navigation to error context
      recordAction(`page_view:${pathname}`, {
        location: document.referrer,
        data: { url },
      });

      // Reset scroll tracking for new page
      lastScrollPosition.current = 0;
      scrollDirection.current = "down";
    }
  }, [pathname, searchParams]);

  return <>{children}</>;
}
