"use client";
/**
 * 特定 Android 设备 + Facebook IAB 专用输入框避让 hook。
 *
 * 这不是通用软键盘方案。它只解决一个已知 WebView 问题：
 * 输入框 focus 后键盘弹出，但 Facebook IAB 没有稳定调整页面 viewport，
 * 导致底部输入框被键盘遮挡。
 *
 * 策略：
 *   - 非目标 Android Facebook IAB 设备：完全不生效。
 *   - focus 后短期多次测量输入框是否被 visibleViewport 遮挡。
 *   - 若 viewport 没有可靠变化，则使用固定 fallback 抬升。
 *   - 聚焦期间轻量轮询，持续保持输入框避让。
 */
import { useEffect } from "react";

import { Logger } from "@/utils";

import {
  FALLBACK_INPUT_LIFT_PX,
  FOCUS_GAP_PX,
  KEYBOARD_MEASURE_DELAYS_MS,
  KEYBOARD_POLL_INTERVAL_MS,
  KEYBOARD_VISIBLE_RATIO,
  KEYBOARD_VISIBLE_THRESHOLD_PX,
} from "./constants";
import {
  resetInputLiftVar,
  setInputLiftVar,
} from "./css-vars";
import {
  getKeyboardAdaptEnvironment,
  shouldEnableKeyboardAdaptation,
} from "./environment";
import { isKeyboardTargetActive } from "./keyboard-target";
import type { UseKeyboardHeightOptions } from "./types";

const log = new Logger("HooksUseKeyboardHeight");

interface KeyboardSnapshot {
  layoutHeight: number;
  visibleHeight: number;
  visibleBottom: number;
  keyboardHeight: number;
  keyboardVisible: boolean;
}

export function useKeyboardHeight({
  targetRef,
  active = false,
}: UseKeyboardHeightOptions = {}): void {
  useEffect(() => {
    if (typeof window === "undefined") return;

    const environment = getKeyboardAdaptEnvironment();
    if (!shouldEnableKeyboardAdaptation(environment)) {
      resetInputLiftVar();
      log.debug("[keyboard] disabled", {
        reason: "not-android-facebook-fallback-device",
        environment,
      });
      return;
    }

    let rafId: number | null = null;
    let pollId: number | null = null;
    let timeoutIds: number[] = [];
    let lastInputLift = -1;

    log.debug("[keyboard] environment", environment);

    const clearTimers = () => {
      timeoutIds.forEach((id) => window.clearTimeout(id));
      timeoutIds = [];
      if (pollId != null) {
        window.clearInterval(pollId);
        pollId = null;
      }
    };

    const applyLift = (lift: number, reason: string) => {
      const nextLift = Math.max(0, Math.round(lift));
      if (nextLift === lastInputLift) return;

      lastInputLift = nextLift;
      setInputLiftVar(nextLift);
      log.debug("[keyboard] apply", {
        reason,
        inputLift: nextLift,
        fallback: nextLift === FALLBACK_INPUT_LIFT_PX,
        snapshot: formatSnapshot(readKeyboardSnapshot()),
      });
    };

    const measure = (reason: string) => {
      rafId = null;

      const target = targetRef?.current;
      const activeNow = isKeyboardTargetActive(target, active);
      const snapshot = readKeyboardSnapshot();

      if (!activeNow) {
        log.debug("[keyboard] skip inactive target", {
          reason,
          snapshot: formatSnapshot(snapshot),
        });
        return;
      }

      const overlapLift = computeTargetOverlap(target, snapshot.visibleBottom);
      const fallbackLift = snapshot.keyboardVisible ? 0 : FALLBACK_INPUT_LIFT_PX;
      applyLift(Math.max(overlapLift, fallbackLift), reason);
    };

    const scheduleMeasure = (reason: string) => {
      if (rafId != null) return;
      rafId = window.requestAnimationFrame(() => measure(reason));
    };

    const scheduleInitialMeasures = () => {
      timeoutIds = KEYBOARD_MEASURE_DELAYS_MS.map((delay) =>
        window.setTimeout(() => scheduleMeasure(`focus-${delay}`), delay),
      );
    };

    const handleViewportSignal = () => {
      scheduleMeasure("viewport-signal");
    };

    if (!active) {
      return;
    }

    const visualViewport = window.visualViewport;
    visualViewport?.addEventListener("resize", handleViewportSignal);
    visualViewport?.addEventListener("scroll", handleViewportSignal);
    window.addEventListener("resize", handleViewportSignal);
    window.addEventListener("orientationchange", handleViewportSignal);
    document.addEventListener("visibilitychange", handleViewportSignal);

    scheduleInitialMeasures();
    pollId = window.setInterval(
      () => scheduleMeasure("poll"),
      KEYBOARD_POLL_INTERVAL_MS,
    );

    return () => {
      if (rafId != null) window.cancelAnimationFrame(rafId);
      clearTimers();
      visualViewport?.removeEventListener("resize", handleViewportSignal);
      visualViewport?.removeEventListener("scroll", handleViewportSignal);
      window.removeEventListener("resize", handleViewportSignal);
      window.removeEventListener("orientationchange", handleViewportSignal);
      document.removeEventListener("visibilitychange", handleViewportSignal);
      resetInputLiftVar();
    };
  }, [active, targetRef]);
}

function readKeyboardSnapshot(): KeyboardSnapshot {
  const layoutHeight = window.innerHeight;
  const visualViewport = window.visualViewport;
  const visibleHeight = visualViewport?.height ?? layoutHeight;
  const visibleBottom = visualViewport
    ? visualViewport.offsetTop + visualViewport.height
    : layoutHeight;
  const keyboardHeight = Math.max(0, layoutHeight - visibleBottom);
  const keyboardVisible =
    keyboardHeight > KEYBOARD_VISIBLE_THRESHOLD_PX ||
    visibleHeight < layoutHeight * KEYBOARD_VISIBLE_RATIO;

  return {
    layoutHeight,
    visibleHeight,
    visibleBottom,
    keyboardHeight,
    keyboardVisible,
  };
}

function computeTargetOverlap(
  target: HTMLElement | null | undefined,
  visibleBottom: number,
): number {
  if (!target) return 0;

  const rect = target.getBoundingClientRect();
  return Math.max(0, rect.bottom + FOCUS_GAP_PX - visibleBottom);
}

function formatSnapshot(snapshot: KeyboardSnapshot) {
  return {
    layoutHeight: Math.round(snapshot.layoutHeight),
    visibleHeight: Math.round(snapshot.visibleHeight),
    visibleBottom: Math.round(snapshot.visibleBottom),
    keyboardHeight: Math.round(snapshot.keyboardHeight),
    keyboardVisible: snapshot.keyboardVisible,
  };
}

export type { UseKeyboardHeightOptions };
