'use client';
import { useCallback, useState } from 'react';
import { useTranslations } from 'next-intl';
import { useSnackbar } from 'notistack';
import { ApiError } from '@/lib/api/errors';
import { useCheckInVisit, useCheckOutVisit } from '@/services/bookings';
import { locationProvider } from '@/services/bookings/evv/locationProvider';

export interface EvvTarget {
  sessionId: number;
  bookingId: number;
}

/**
 * Orchestrates an EVV check-in/out for a session: capture GPS through the `ILocationProvider` seam, then
 * post via the `useCheckInVisit`/`useCheckOutVisit` mutations (which invalidate the detail/today/EVV
 * queries so the timeline, session row, and banner re-render from server truth). Tracks the acting
 * session so only its card shows the "acquiring…" / pending state.
 *
 * **GPS is never a hard stop** — a denied/unavailable fix (`null`) still checks in (flagged), with an
 * advisory toast. A mismatch is advisory server-side (the banner warns). Only domain 4xx are toasted here
 * (409 not-startable, 400 no-open-check-in); `clientFetch` already toasts 401/403/5xx/network.
 */
export function useEvvController() {
  const t = useTranslations('booking');
  const { enqueueSnackbar } = useSnackbar();
  const checkInMut = useCheckInVisit();
  const checkOutMut = useCheckOutVisit();
  const [acquiringSessionId, setAcquiringSessionId] = useState<number | null>(null);
  const [busySessionId, setBusySessionId] = useState<number | null>(null);

  const capture = useCallback(async (sessionId: number) => {
    setAcquiringSessionId(sessionId);
    const pos = await locationProvider.getCurrentPosition();
    setAcquiringSessionId(null);
    return pos;
  }, []);

  const checkIn = useCallback(
    async ({ sessionId, bookingId }: EvvTarget) => {
      const pos = await capture(sessionId);
      setBusySessionId(sessionId);
      try {
        await checkInMut.mutateAsync({
          input: {
            bookingSessionId: sessionId,
            latitude: pos?.latitude ?? null,
            longitude: pos?.longitude ?? null,
            capturedAt: new Date().toISOString(),
          },
          bookingId,
        });
        enqueueSnackbar(t('evv_checked_in_toast'), { variant: 'success' });
        // GPS denied/unavailable is advisory, not a block — surface it without failing the check-in.
        if (!pos) enqueueSnackbar(t('evv_gps_denied_note'), { variant: 'warning' });
      } catch (error) {
        if (error instanceof ApiError && error.status === 409) {
          enqueueSnackbar(t('evv_not_startable'), { variant: 'warning' });
        }
      } finally {
        setBusySessionId(null);
      }
    },
    [capture, checkInMut, enqueueSnackbar, t],
  );

  const checkOut = useCallback(
    async ({ sessionId, bookingId }: EvvTarget) => {
      const pos = await capture(sessionId);
      setBusySessionId(sessionId);
      try {
        await checkOutMut.mutateAsync({
          input: {
            bookingSessionId: sessionId,
            latitude: pos?.latitude ?? null,
            longitude: pos?.longitude ?? null,
            capturedAt: new Date().toISOString(),
          },
          bookingId,
        });
        enqueueSnackbar(t('evv_checked_out_toast'), { variant: 'success' });
      } catch (error) {
        if (error instanceof ApiError && (error.status === 400 || error.code === 'no_open_check_in')) {
          enqueueSnackbar(t('evv_no_open_check_in'), { variant: 'warning' });
        } else if (error instanceof ApiError && error.status === 409) {
          enqueueSnackbar(t('evv_check_out_error'), { variant: 'warning' });
        }
      } finally {
        setBusySessionId(null);
      }
    },
    [capture, checkOutMut, enqueueSnackbar, t],
  );

  return { acquiringSessionId, busySessionId, checkIn, checkOut };
}
