import { useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
import { Link } from "react-router";
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
import { Avatar } from "./Avatar.tsx";
import { useAuth } from "../hooks/useAuth.ts";
import { DEFAULT_PAGE_SIZE } from "../config/api.ts";
import {
  deserializePublicUser,
  type PaginatedData,
  parseAPIResponse,
  type PublicUser,
  type RawPublicUser,
} from "../model.ts";

interface UserListPopoverProps {
  anchorRef: React.RefObject<HTMLElement | null>;
  onClose: () => void;
  fetchUrl: string;
}

type State =
  | { status: "loading" }
  | { status: "error"; error: string }
  | {
    status: "loaded";
    users: PublicUser[];
    page: number;
    hasMore: boolean;
    loadingMore: boolean;
  };

export function UserListPopover(
  { anchorRef, onClose, fetchUrl }: UserListPopoverProps,
) {
  const { token } = useAuth();
  const [state, setState] = useState<State>({ status: "loading" });
  const [rect, setRect] = useState<DOMRect | null>(null);
  const popoverRef = useRef<HTMLDivElement>(null);

  // Position via the anchor's viewport rect (mirrors Tooltip.tsx) — fixed +
  // portaled to <body> so the popover can't be clipped by a card's overflow.
  useEffect(() => {
    setRect(anchorRef.current?.getBoundingClientRect() ?? null);
  }, [anchorRef]);

  useEffect(() => {
    function onMouseDown(e: MouseEvent) {
      const target = e.target as Node;
      if (
        popoverRef.current?.contains(target) ||
        anchorRef.current?.contains(target)
      ) {
        return;
      }
      onClose();
    }
    function onKeyDown(e: KeyboardEvent) {
      if (e.key === "Escape") onClose();
    }
    function onScroll() {
      onClose();
    }
    document.addEventListener("mousedown", onMouseDown);
    document.addEventListener("keydown", onKeyDown);
    window.addEventListener("scroll", onScroll, true);
    return () => {
      document.removeEventListener("mousedown", onMouseDown);
      document.removeEventListener("keydown", onKeyDown);
      window.removeEventListener("scroll", onScroll, true);
    };
  }, [onClose, anchorRef]);

  useEffect(() => {
    const controller = new AbortController();
    fetch(`${fetchUrl}?page=1&limit=${DEFAULT_PAGE_SIZE}`, {
      signal: controller.signal,
      headers: token ? { Authorization: `Bearer ${token}` } : {},
    })
      .then((r) => r.json())
      .then((body) => {
        const apiResponse = parseAPIResponse<PaginatedData<RawPublicUser>>(
          body,
        );
        if (!apiResponse.success) {
          setState({ status: "error", error: apiResponse.error.message });
          return;
        }
        setState({
          status: "loaded",
          users: apiResponse.data.items.map(deserializePublicUser),
          page: 1,
          hasMore: apiResponse.data.hasMore,
          loadingMore: false,
        });
      })
      .catch(() => {
        if (controller.signal.aborted) return;
        setState({ status: "error", error: t`Could not load.` });
      });
    return () => controller.abort();
  }, [fetchUrl, token]);

  const handleLoadMore = () => {
    if (state.status !== "loaded" || state.loadingMore) return;
    const nextPage = state.page + 1;
    setState({ ...state, loadingMore: true });
    fetch(`${fetchUrl}?page=${nextPage}&limit=${DEFAULT_PAGE_SIZE}`, {
      headers: token ? { Authorization: `Bearer ${token}` } : {},
    })
      .then((r) => r.json())
      .then((body) => {
        const apiResponse = parseAPIResponse<PaginatedData<RawPublicUser>>(
          body,
        );
        setState((prev) => {
          if (prev.status !== "loaded") return prev;
          if (!apiResponse.success) return { ...prev, loadingMore: false };
          return {
            status: "loaded",
            users: [
              ...prev.users,
              ...apiResponse.data.items.map(deserializePublicUser),
            ],
            page: nextPage,
            hasMore: apiResponse.data.hasMore,
            loadingMore: false,
          };
        });
      });
  };

  if (!rect) return null;

  return createPortal(
    <div
      className="user-list-popover"
      role="menu"
      ref={popoverRef}
      style={{
        position: "fixed",
        top: rect.bottom + 4,
        left: rect.left,
      }}
    >
      {state.status === "loading" && (
        <p className="user-list-popover-status">
          <Trans>Loading…</Trans>
        </p>
      )}
      {state.status === "error" && (
        <p className="user-list-popover-status">{state.error}</p>
      )}
      {state.status === "loaded" && state.users.length === 0 && (
        <p className="user-list-popover-status">
          <Trans>No one yet.</Trans>
        </p>
      )}
      {state.status === "loaded" && state.users.length > 0 && (
        <>
          {state.users.map((u) => (
            <Link
              key={u.id}
              to={`/users/${u.username}`}
              className="user-list-popover-item"
              role="menuitem"
              onClick={onClose}
            >
              <Avatar
                userId={u.id}
                username={u.username}
                hasAvatar={!!u.avatarMime}
                size={24}
              />
              <span>{u.username}</span>
            </Link>
          ))}
          {state.hasMore && (
            <button
              type="button"
              className="user-list-popover-load-more"
              onClick={handleLoadMore}
              disabled={state.loadingMore}
            >
              {state.loadingMore
                ? <Trans>Loading…</Trans>
                : <Trans>Load more</Trans>}
            </button>
          )}
        </>
      )}
    </div>,
    document.body,
  );
}
