import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useClient } from 'hooks/useClient';
import type { HeadscaleInviteCreated, InviteCreateInput, InviteCreateResult, InvitesListResult } from './shared';

// Device invites for the active server. Everything goes through the headscale sidecar, which proxies the
// server's own companion — see src/servers/sidecar/headscale/invites.ts for why the records live there and
// not here.
//
// The create result is deliberately NOT merged into the list cache. It is the one response that contains the
// claim link, and a cache is a place things persist: the view keeps it in component state, shows it once and
// drops it. The list is refetched instead, which returns the same invite without its token.

const BASE = '/offscale/_officer/enroll/invites';
const INVITES_KEY = ['headscale', 'invites'] as const;

const EMPTY: InvitesListResult = { available: true, invites: [] };

export function useHeadscaleInvites() {
  const { get, post, delete: del } = useClient();
  const qc = useQueryClient();
  const invalidate = () => qc.invalidateQueries({ queryKey: INVITES_KEY });

  const query = useQuery({
    queryKey: INVITES_KEY,
    queryFn: () => get<InvitesListResult>(BASE),
    // A pending invite expires on a clock, so a list left open goes wrong on its own. Cheap: one companion
    // call against a table with a handful of rows.
    refetchInterval: 30_000,
    staleTime: 10_000,
  });

  const create = useMutation({
    mutationFn: async (input: InviteCreateInput): Promise<HeadscaleInviteCreated> => {
      const result = await post<InviteCreateResult>(BASE, input);
      // An unavailable companion is a 200 here, like every companion route — turn it into a rejection so the
      // form shows it where the admin is looking rather than rendering an empty link panel.
      if (!result.available) throw new Error(result.reason);
      return result.invite;
    },
    onSuccess: invalidate,
  });

  const revoke = useMutation({
    mutationFn: (id: string) => del(`${BASE}/${encodeURIComponent(id)}`),
    onSuccess: invalidate,
  });

  const result = query.data ?? EMPTY;

  return {
    invites: result.available ? result.invites : [],
    /** Set when this server has no enrolment API — a state to explain, not an error. */
    unavailable: result.available ? null : result.reason,
    isLoading: query.isLoading,
    error: query.error,
    create,
    revoke,
  };
}
