/**
 * Admin page hook: BackButton, admin check, users/duties loading, reassign sheet state,
 * infinite scroll, and derived data. Used by the admin page for composition.
 */

"use client";

import { useEffect, useState, useCallback, useRef } from "react";
import { useRouter } from "next/navigation";
import { backButton } from "@telegram-apps/sdk-react";
import { useTelegramSdkReady } from "@/components/providers/TelegramProvider";
import { useAppStore } from "@/store/app-store";
import { useShallow } from "zustand/react/shallow";
import { useTelegramAuth } from "@/hooks/use-telegram-auth";
import { useTranslation } from "@/i18n/use-translation";
import {
  fetchDuties,
  fetchAdminMe,
  fetchAdminUsers,
  patchAdminDuty,
  AccessDeniedError,
  type UserForAdmin,
} from "@/lib/api";
import { triggerHapticLight } from "@/lib/telegram-haptic";
import type { DutyWithUser } from "@/types";
import {
  firstDayOfMonth,
  lastDayOfMonth,
  localDateString,
} from "@/lib/date-utils";

const PAGE_SIZE = 20;

export function useAdminPage() {
  const router = useRouter();
  const { sdkReady } = useTelegramSdkReady();
  const { initDataRaw, isLocalhost } = useTelegramAuth();
  const isAllowed = isLocalhost || !!initDataRaw;

  const { lang } = useAppStore(useShallow((s) => ({ lang: s.lang })));
  const currentMonth = useAppStore((s) => s.currentMonth);
  const { t } = useTranslation();

  /** Local month for admin view; does not change global calendar month. */
  const [adminMonth, setAdminMonth] = useState<Date>(() => new Date(currentMonth.getFullYear(), currentMonth.getMonth(), 1));

  const [users, setUsers] = useState<UserForAdmin[]>([]);
  const [duties, setDuties] = useState<DutyWithUser[]>([]);
  const [loadingUsers, setLoadingUsers] = useState(true);
  const [loadingDuties, setLoadingDuties] = useState(true);
  const [adminCheckComplete, setAdminCheckComplete] = useState<boolean | null>(null);
  const [adminAccessDenied, setAdminAccessDenied] = useState(false);
  const [adminAccessDeniedDetail, setAdminAccessDeniedDetail] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [selectedDuty, setSelectedDuty] = useState<DutyWithUser | null>(null);
  const [selectedUserId, setSelectedUserId] = useState<number | "">("");
  const [saving, setSaving] = useState(false);
  const [reassignErrorKey, setReassignErrorKey] = useState<string | null>(null);
  const [successMessage, setSuccessMessage] = useState<string | null>(null);
  const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
  const [sheetExiting, setSheetExiting] = useState(false);
  const sentinelRef = useRef<HTMLDivElement | null>(null);
  const navigateHomeRef = useRef(() => router.push("/"));
  navigateHomeRef.current = () => router.push("/");

  const from = localDateString(firstDayOfMonth(adminMonth));
  const to = localDateString(lastDayOfMonth(adminMonth));

  const onPrevMonth = useCallback(() => {
    setAdminMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1));
  }, []);
  const onNextMonth = useCallback(() => {
    setAdminMonth((prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1));
  }, []);

  useEffect(() => {
    if (!sdkReady || isLocalhost) return;
    let offClick: (() => void) | undefined;
    try {
      if (backButton.mount.isAvailable()) {
        backButton.mount();
      }
      if (backButton.show.isAvailable()) {
        backButton.show();
      }
      if (backButton.onClick.isAvailable()) {
        offClick = backButton.onClick(() => navigateHomeRef.current());
      }
    } catch {
      // Non-Telegram environment; BackButton not available.
    }
    return () => {
      try {
        if (typeof offClick === "function") offClick();
        if (backButton.hide.isAvailable()) {
          backButton.hide();
        }
      } catch {
        // Ignore cleanup errors in non-Telegram environment.
      }
    };
  }, [sdkReady, isLocalhost, router]);

  useEffect(() => {
    if (!isAllowed || !initDataRaw) return;
    setAdminCheckComplete(null);
    setAdminAccessDenied(false);
    fetchAdminMe(initDataRaw, lang)
      .then(({ is_admin }) => {
        if (!is_admin) {
          setAdminAccessDenied(true);
          setAdminAccessDeniedDetail(null);
          setAdminCheckComplete(false);
        } else {
          setAdminCheckComplete(true);
        }
      })
      .catch(() => {
        setAdminAccessDenied(true);
        setAdminAccessDeniedDetail(null);
        setAdminCheckComplete(false);
      });
  }, [isAllowed, initDataRaw, lang]);

  useEffect(() => {
    if (!isAllowed || !initDataRaw || adminCheckComplete !== true) return;
    const controller = new AbortController();
    setLoadingUsers(true);
    fetchAdminUsers(initDataRaw, lang, controller.signal)
      .then((list) => setUsers(list))
      .catch((e) => {
        if ((e as Error)?.name === "AbortError") return;
        if (e instanceof AccessDeniedError) {
          setAdminAccessDenied(true);
          setAdminAccessDeniedDetail(e.serverDetail ?? null);
        } else {
          setError(e instanceof Error ? e.message : String(e));
        }
      })
      .finally(() => setLoadingUsers(false));
    return () => controller.abort();
  }, [isAllowed, initDataRaw, lang, adminCheckComplete]);

  useEffect(() => {
    if (!isAllowed || !initDataRaw || adminCheckComplete !== true) return;
    const controller = new AbortController();
    setLoadingDuties(true);
    setError(null);
    fetchDuties(from, to, initDataRaw, lang, controller.signal)
      .then((list) => setDuties(list))
      .catch((e) => {
        if ((e as Error)?.name === "AbortError") return;
        setError(e instanceof Error ? e.message : String(e));
      })
      .finally(() => setLoadingDuties(false));
    return () => controller.abort();
  }, [isAllowed, initDataRaw, lang, from, to, adminCheckComplete]);

  useEffect(() => {
    setVisibleCount(PAGE_SIZE);
  }, [from, to]);

  const closeReassign = useCallback(() => {
    setSelectedDuty(null);
    setSelectedUserId("");
    setReassignErrorKey(null);
    setSheetExiting(false);
  }, []);

  useEffect(() => {
    if (!sheetExiting) return;
    const fallback = window.setTimeout(() => {
      closeReassign();
    }, 320);
    return () => window.clearTimeout(fallback);
  }, [sheetExiting, closeReassign]);

  const openReassign = useCallback((duty: DutyWithUser) => {
    setSelectedDuty(duty);
    setSelectedUserId(duty.user_id);
    setReassignErrorKey(null);
  }, []);

  const requestCloseSheet = useCallback(() => {
    setSheetExiting(true);
  }, []);

  const handleReassign = useCallback(() => {
    if (!selectedDuty || selectedUserId === "" || !initDataRaw) return;
    if (selectedUserId === selectedDuty.user_id) {
      closeReassign();
      return;
    }
    setSaving(true);
    setReassignErrorKey(null);
    patchAdminDuty(selectedDuty.id, selectedUserId, initDataRaw, lang)
      .then((updated) => {
        setDuties((prev) =>
          prev.map((d) =>
            d.id === updated.id
              ? {
                  ...d,
                  user_id: updated.user_id,
                  full_name:
                    users.find((u) => u.id === updated.user_id)?.full_name ?? d.full_name,
                }
              : d
          )
        );
        setSuccessMessage(t("admin.reassign_success"));
        try {
          triggerHapticLight();
        } catch {
          // Haptic not available (e.g. non-Telegram).
        }
        requestCloseSheet();
        setTimeout(() => setSuccessMessage(null), 3000);
      })
      .catch((e) => {
        if (e instanceof AccessDeniedError) {
          setReassignErrorKey("admin.reassign_error_denied");
        } else if (e instanceof Error && /not found|не найден/i.test(e.message)) {
          setReassignErrorKey("admin.reassign_error_not_found");
        } else if (
          e instanceof TypeError ||
          (e instanceof Error && (e.message === "Failed to fetch" || e.message === "Load failed"))
        ) {
          setReassignErrorKey("admin.reassign_error_network");
        } else {
          setReassignErrorKey("admin.reassign_error_generic");
        }
      })
      .finally(() => setSaving(false));
  }, [selectedDuty, selectedUserId, initDataRaw, lang, users, closeReassign, requestCloseSheet, t]);

  const now = new Date();
  const dutyOnly = duties
    .filter((d) => d.event_type === "duty" && new Date(d.end_at) > now)
    .sort(
      (a, b) =>
        new Date(a.start_at).getTime() - new Date(b.start_at).getTime()
    );

  const usersForSelect = users.filter((u) => u.role_id === 1 || u.role_id === 2);
  const visibleDuties = dutyOnly.slice(0, visibleCount);
  const hasMore = visibleCount < dutyOnly.length;
  const loading = loadingUsers || loadingDuties;

  /** Group visible duties by date (dateKey) for sectioned list. Order preserved by insertion. */
  const visibleGroups = (() => {
    const map = new Map<string, DutyWithUser[]>();
    for (const d of visibleDuties) {
      const key = localDateString(new Date(d.start_at));
      if (!map.has(key)) map.set(key, []);
      map.get(key)!.push(d);
    }
    return Array.from(map.entries()).map(([dateKey, duties]) => ({ dateKey, duties }));
  })();

  useEffect(() => {
    if (!hasMore || !sentinelRef.current) return;
    const el = sentinelRef.current;
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0]?.isIntersecting) {
          setVisibleCount((prev) => Math.min(prev + PAGE_SIZE, dutyOnly.length));
        }
      },
      { root: null, rootMargin: "200px", threshold: 0 }
    );
    observer.observe(el);
    return () => observer.disconnect();
  }, [hasMore, dutyOnly.length]);

  return {
    isAllowed,
    adminCheckComplete,
    adminAccessDenied,
    adminAccessDeniedDetail,
    error,
    loading,
    adminMonth,
    onPrevMonth,
    onNextMonth,
    successMessage,
    dutyOnly,
    usersForSelect,
    visibleDuties,
    visibleGroups,
    hasMore,
    sentinelRef,
    selectedDuty,
    selectedUserId,
    setSelectedUserId,
    saving,
    reassignErrorKey,
    sheetExiting,
    openReassign,
    requestCloseSheet,
    handleReassign,
    closeReassign,
  };
}
