"use client";

import { useEffect, useRef, type RefObject } from "react";

import {
  isViewportGeometryObservation,
  observeBrowserViewport,
  readBrowserViewportMetrics,
  type BrowserViewportMetrics,
  type ViewportObservationReason,
} from "@/lib/browser/viewport_observer";
import { BrowserDetector } from "@/utils/browser-detect";
import { Logger } from "@/utils/logger";
import { PlatformDetector } from "@/utils/platform-detect";

import {
  resolveChatKeyboardFallbackInset,
  resolveChatKeyboardInset,
  type ChatKeyboardInsetResolution,
  type ChatKeyboardViewportBaseline,
  type ChatKeyboardViewportSnapshot,
} from "./chat-keyboard-geometry";

const CHAT_KEYBOARD_BOTTOM_INSET_VAR = "--chat-keyboard-bottom-inset";
const CHAT_KEYBOARD_FALLBACK_DELAY_MS = 150;

const log = new Logger("UseChatKeyboardAvoidance");

export interface UseChatKeyboardAvoidanceOptions {
  active: boolean;
  containerRef: RefObject<HTMLElement | null>;
}

export function useChatKeyboardAvoidance({
  active,
  containerRef,
}: UseChatKeyboardAvoidanceOptions): void {
  const activeRef = useRef(active);
  const baselineRef = useRef<ChatKeyboardViewportBaseline | null>(null);

  useEffect(() => {
    activeRef.current = active;
  }, [active]);

  useEffect(() => {
    if (typeof window === "undefined" || !isKeyboardAvoidanceEnabled()) {
      return;
    }

    return observeBrowserViewport((observation) => {
      if (
        activeRef.current ||
        !isViewportGeometryObservation(observation.reason)
      ) {
        return;
      }
      baselineRef.current = toKeyboardBaseline(observation.metrics);
    });
  }, []);

  useEffect(() => {
    const container = containerRef.current;
    if (typeof window === "undefined" || !container) return;

    const enabled = isKeyboardAvoidanceEnabled();
    if (!active || !enabled) {
      clearBottomInset(container);
      if (!active && enabled) {
        baselineRef.current = toKeyboardBaseline(
          readBrowserViewportMetrics(),
        );
      }
      return;
    }

    const baseline =
      baselineRef.current ??
      toKeyboardBaseline(readBrowserViewportMetrics());
    let frameId: number | null = null;
    let fallbackTimerId: number | null = null;
    let lastInset = -1;
    let pendingCanDismiss = false;
    let pendingReason = "initial";
    let pendingMetrics = readBrowserViewportMetrics();
    let keyboardObserved = false;
    let fallbackApplied = false;
    let sessionDismissed = false;

    const applyBottomInset = (
      bottomInset: number,
      source: ChatKeyboardInsetResolution["source"] | "fallback" | "reset",
    ) => {
      const nextInset = Math.max(0, Math.round(bottomInset));
      if (nextInset === lastInset) return;

      lastInset = nextInset;
      container.style.setProperty(
        CHAT_KEYBOARD_BOTTOM_INSET_VAR,
        `${nextInset}px`,
      );
      log.debug("[chat-keyboard] apply", {
        source,
        bottomInset: nextInset,
      });
    };

    const clearFallbackTimer = () => {
      if (fallbackTimerId == null) return;
      window.clearTimeout(fallbackTimerId);
      fallbackTimerId = null;
    };

    const measure = (
      reason: string,
      canDismiss: boolean,
      metrics: BrowserViewportMetrics,
    ) => {
      if (sessionDismissed) return;

      const resolution = resolveChatKeyboardInset(
        baseline,
        toKeyboardSnapshot(metrics),
      );

      if (resolution.keyboardVisible) {
        keyboardObserved = true;
        clearFallbackTimer();
        applyBottomInset(resolution.bottomInset, resolution.source);
        return;
      }

      if ((keyboardObserved || fallbackApplied) && canDismiss) {
        sessionDismissed = true;
        clearFallbackTimer();
        applyBottomInset(0, "reset");
        log.debug("[chat-keyboard] dismissed", { reason });
      }
    };

    const scheduleMeasure = (
      reason: string,
      canDismiss: boolean,
      metrics: BrowserViewportMetrics,
    ) => {
      pendingReason = reason;
      pendingCanDismiss ||= canDismiss;
      pendingMetrics = metrics;
      if (frameId != null) return;

      frameId = window.requestAnimationFrame(() => {
        frameId = null;
        const nextReason = pendingReason;
        const nextCanDismiss = pendingCanDismiss;
        pendingCanDismiss = false;
        measure(nextReason, nextCanDismiss, pendingMetrics);
      });
    };

    const resetSession = (reason: string) => {
      sessionDismissed = true;
      clearFallbackTimer();
      applyBottomInset(0, "reset");
      log.debug("[chat-keyboard] reset", { reason });
    };
    const stopObserving = observeBrowserViewport((observation) => {
      if (observation.reason === "orientationchange") {
        resetSession(observation.reason);
        return;
      }
      if (
        observation.reason === "visibilitychange" &&
        document.visibilityState === "hidden"
      ) {
        resetSession(observation.reason);
        return;
      }
      if (!isViewportGeometryObservation(observation.reason)) return;

      scheduleMeasure(
        observation.reason === "initial" ? "focus" : observation.reason,
        canDismissKeyboardSession(observation.reason),
        observation.metrics,
      );
    });
    fallbackTimerId = window.setTimeout(() => {
      fallbackTimerId = null;
      if (!activeRef.current || sessionDismissed) return;

      const resolution = resolveChatKeyboardInset(
        baseline,
        toKeyboardSnapshot(readBrowserViewportMetrics()),
      );
      if (resolution.keyboardVisible) {
        keyboardObserved = true;
        applyBottomInset(resolution.bottomInset, resolution.source);
        return;
      }
      fallbackApplied = true;
      applyBottomInset(
        resolveChatKeyboardFallbackInset(baseline),
        "fallback",
      );
    }, CHAT_KEYBOARD_FALLBACK_DELAY_MS);

    return () => {
      if (frameId != null) window.cancelAnimationFrame(frameId);
      clearFallbackTimer();
      stopObserving();
      clearBottomInset(container);
    };
  }, [active, containerRef]);
}

function toKeyboardBaseline(
  metrics: BrowserViewportMetrics,
): ChatKeyboardViewportBaseline {
  return {
    layoutHeight: metrics.layoutHeight,
    layoutWidth: metrics.layoutWidth,
    visualHeight: metrics.visualHeight,
  };
}

function toKeyboardSnapshot(
  metrics: BrowserViewportMetrics,
): ChatKeyboardViewportSnapshot {
  return {
    layoutHeight: metrics.layoutHeight,
    visualHeight: metrics.visualHeight,
    visualScale: metrics.visualScale,
    virtualKeyboardHeight: metrics.virtualKeyboardRect?.height ?? 0,
  };
}

function canDismissKeyboardSession(
  reason: ViewportObservationReason,
): boolean {
  return (
    reason === "window.resize" ||
    reason === "visualViewport.resize" ||
    reason === "virtualKeyboard.geometrychange"
  );
}

function clearBottomInset(container: HTMLElement): void {
  container.style.removeProperty(CHAT_KEYBOARD_BOTTOM_INSET_VAR);
}

function isKeyboardAvoidanceEnabled(): boolean {
  return (
    PlatformDetector.isAndroid() && BrowserDetector.isFacebookInAppBrowser()
  );
}
