import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo, useState } from "react";

import { api } from "@/services/api";
import type { Freight } from "@edr/types";

export type AdHocDoc = { name: string; file: File | null };

/**
 * Encapsulates everything the customer-facing clearance/operation flow needs:
 * the clearance grid query, the staged uploads (keyed pending + ad-hoc docs),
 * the chosen shipment day, and the submit / proceed mutations.
 *
 * Both the booking detail clearance card and the home-page action modal drive
 * their UI off this single hook so the behaviour stays in lock-step.
 */
export function useClearanceFlow(booking: Freight.IBooking) {
  const queryClient = useQueryClient();
  const status = booking.status as string;

  const clearanceQuery = useQuery(
    api.bookings.getClearance.queryOptions({ input: { id: booking.id } }),
  );
  const clearance = clearanceQuery.data;

  // Pending uploads keyed by fileKey, plus ad-hoc rows (label + file).
  const [pending, setPending] = useState<Record<string, File>>({});
  const [adHoc, setAdHoc] = useState<AdHocDoc[]>([]);
  // Binding shipment day chosen for the operation request (yyyy-MM-dd).
  const [scheduledDate, setScheduledDate] = useState<string>("");

  const refresh = () => {
    queryClient.invalidateQueries({
      queryKey: api.bookings.getClearance.queryKey({ id: booking.id }),
    });
    queryClient.invalidateQueries({
      queryKey: api.bookings.get.queryKey({ id: booking.id }),
    });
    queryClient.invalidateQueries({
      queryKey: api.bookings.list.queryKey(),
    });
  };

  const uploadMutation = useMutation({
    ...api.bookings.submitClearanceDocuments.mutationOptions(),
    onSuccess: () => {
      setPending({});
      setAdHoc([]);
      refresh();
    },
  });

  const proceedMutation = useMutation({
    ...api.bookings.proceedToOperation.mutationOptions(),
    onSuccess: () => refresh(),
  });

  // Only the customer-input documents are uploadable here; GL output docs are
  // shown read-only.
  const customerDocs = useMemo(
    () => (clearance?.documents ?? []).filter((d) => d.uploadedBy === "customer"),
    [clearance],
  );
  const glDocs = useMemo(
    () => (clearance?.documents ?? []).filter((d) => d.uploadedBy === "gl"),
    [clearance],
  );

  const isReady = status === "CLEARANCE_READY";
  // Bare initiated instance: created with no cargo and no price; completion
  // (cargo + shipment day + window check) happens on the full booking form.
  const isBareInstance =
    Boolean(booking.contractId) && !(Number(booking.totalAmount ?? 0) > 0);
  // Non-customs (Path A): the CUSTOMER completes the booking himself.
  const needsCompletion =
    isReady && isBareInstance && !clearance?.includesCustoms;
  const completeTo = needsCompletion
    ? `/contracts/${booking.contractId}/bookings/${booking.id}/complete`
    : null;
  // Customs (Path B): GL completes on the customer's behalf — the customer
  // just sees that clearance is done and GL is preparing the booking.
  const awaitingGlCompletion =
    isReady && isBareInstance && Boolean(clearance?.includesCustoms);
  const canUpload =
    status === "AWAITING_DOCUMENTS" || status === "DOCUMENTS_UNDER_REVIEW";
  // The very first upload (nothing in review yet). Here every required document
  // must be provided. Once GL has started reviewing (DOCUMENTS_UNDER_REVIEW) the
  // customer is only re-uploading queried/pending docs, so we don't re-gate on
  // the full required set.
  const isInitialUpload = status === "AWAITING_DOCUMENTS";

  const hasStagedFiles =
    Object.keys(pending).length > 0 || adHoc.some((r) => r.file);

  // A required customer document is satisfied when it already has an uploaded
  // file or the customer has just staged one for this submission.
  const missingRequired = useMemo(
    () =>
      customerDocs.filter(
        (d) => d.required && !d.file && !pending[d.fileKey],
      ),
    [customerDocs, pending],
  );

  // Initial upload: block submit until every required field has a file (and at
  // least one file is actually staged to send). Re-upload rounds only need at
  // least one staged file — the customer fixes the specific queried documents.
  const canSubmit = isInitialUpload
    ? hasStagedFiles && missingRequired.length === 0
    : hasStagedFiles;

  // --- staged-upload mutators ----------------------------------------------

  const stagePending = (fileKey: string, file: File | null) =>
    setPending((p) => {
      if (file) return { ...p, [fileKey]: file };
      const next = { ...p };
      delete next[fileKey];
      return next;
    });

  const addAdHocRow = () => setAdHoc((r) => [...r, { name: "", file: null }]);

  const removeAdHocRow = (index: number) =>
    setAdHoc((rows) => rows.filter((_, j) => j !== index));

  const setAdHocName = (index: number, name: string) =>
    setAdHoc((rows) =>
      rows.map((r, j) => (j === index ? { ...r, name } : r)),
    );

  const setAdHocFile = (index: number, file: File | null) =>
    setAdHoc((rows) =>
      rows.map((r, j) => (j === index ? { ...r, file } : r)),
    );

  // --- actions --------------------------------------------------------------

  const submitDocuments = (opts?: { onSuccess?: () => void }) => {
    const files: Record<string, File | null> = { ...pending };
    adHoc.forEach((row, i) => {
      if (row.file) files[`custom_${Date.now()}_${i}`] = row.file;
    });
    if (Object.keys(files).length === 0) return;
    uploadMutation.mutate({ id: booking.id, files }, { onSuccess: opts?.onSuccess });
  };

  const proceedToOperation = (opts?: { onSuccess?: () => void }) => {
    if (!scheduledDate) return;
    proceedMutation.mutate(
      { id: booking.id, scheduledDate },
      { onSuccess: opts?.onSuccess },
    );
  };

  return {
    status,
    clearance,
    isLoading: clearanceQuery.isLoading,
    customerDocs,
    glDocs,
    isReady,
    needsCompletion,
    completeTo,
    awaitingGlCompletion,
    canUpload,
    isInitialUpload,
    // staged upload state
    pending,
    adHoc,
    hasStagedFiles,
    missingRequired,
    canSubmit,
    stagePending,
    addAdHocRow,
    removeAdHocRow,
    setAdHocName,
    setAdHocFile,
    // schedule
    scheduledDate,
    setScheduledDate,
    // mutations
    uploadMutation,
    proceedMutation,
    submitDocuments,
    proceedToOperation,
  };
}

export type ClearanceFlowController = ReturnType<typeof useClearanceFlow>;
