import { ReactNode, useCallback, useState } from 'react';
import { MatrixClient, Room } from 'matrix-js-sdk';
import { useQuery } from '@tanstack/react-query';
import { IHierarchyRoom } from 'matrix-js-sdk/lib/@types/spaces';
import { useMatrixClient } from '../hooks/useMatrixClient';
import { LocalRoomSummary, useLocalRoomSummary } from '../hooks/useLocalRoomSummary';
import { AsyncState, AsyncStatus } from '../hooks/useAsyncCallback';

// MSC3266 returns canonical_alias at runtime, but the SDK's RoomSummary type
// Omit<>s it — add it back so callers can use it.
export type IRoomSummary = Awaited<ReturnType<MatrixClient['getRoomSummary']>> & {
  canonical_alias?: string;
};

export type RoomSummaryState = { loading: boolean; error: unknown };

type RoomSummaryLoaderProps = {
  roomIdOrAlias: string;
  /**
   * Resident servers to route the summary request through. REQUIRED for a room
   * the local homeserver isn't already in (the exact case a preview exists for) —
   * without it the summary comes back sparse or 404s.
   */
  via?: string[];
  children: (roomSummary: IRoomSummary | undefined, state: RoomSummaryState) => ReactNode;
};

export function RoomSummaryLoader({ roomIdOrAlias, via, children }: RoomSummaryLoaderProps) {
  const mx = useMatrixClient();

  const fetchSummary = useCallback(
    () => mx.getRoomSummary(roomIdOrAlias, via),
    [mx, roomIdOrAlias, via],
  );

  const { data, isLoading, error } = useQuery({
    // `via` is part of the key so a no-via failure isn't reused once we have servers.
    queryKey: [roomIdOrAlias, 'summary', via ?? []],
    queryFn: fetchSummary,
    retry: 1,
    staleTime: 5 * 60 * 1000,
  });

  return children(data, { loading: isLoading, error });
}

export function LocalRoomSummaryLoader({
  room,
  children,
}: {
  room: Room;
  children: (roomSummary: LocalRoomSummary) => ReactNode;
}) {
  const summary = useLocalRoomSummary(room);

  return children(summary);
}

export function HierarchyRoomSummaryLoader({
  roomId,
  children,
}: {
  roomId: string;
  children: (state: AsyncState<IHierarchyRoom, Error>) => ReactNode;
}) {
  const mx = useMatrixClient();

  const fetchSummary = useCallback(() => mx.getRoomHierarchy(roomId, 1, 1), [mx, roomId]);
  const [errorMemo, setError] = useState<Error>();

  const { data, error } = useQuery({
    queryKey: [roomId, `hierarchy`],
    queryFn: fetchSummary,
    retryOnMount: false,
    refetchOnWindowFocus: false,
    retry: (failureCount, err) => {
      setError(err);
      if (failureCount > 3) return false;
      return true;
    },
  });

  let state: AsyncState<IHierarchyRoom, Error> = {
    status: AsyncStatus.Loading,
  };
  if (error) {
    state = {
      status: AsyncStatus.Error,
      error,
    };
  }
  if (errorMemo) {
    state = {
      status: AsyncStatus.Error,
      error: errorMemo,
    };
  }

  const summary = data?.rooms[0] ?? undefined;
  if (summary) {
    state = {
      status: AsyncStatus.Success,
      data: summary,
    };
  }

  return children(state);
}
