import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import type {
  ConsumePart,
  LaborTimeCreate,
  PartCreate,
  PartnerCreate,
  PartnerUpdate,
  PartUpdate,
  PurchaseOrderCreate,
  PurchaseOrderTransition,
  StockMovementCreate,
} from '@siop/shared';
import { api, getToken } from './client';

/** Hooks R3 — client typé ; l'upload passe par FormData (multipart). */

async function unwrap<T>(res: { data?: T; error?: unknown; response: Response }): Promise<T> {
  if (res.error || res.data === undefined) {
    const message =
      (res.error as { message?: string } | undefined)?.message ??
      `Le serveur a répondu ${res.response.status}`;
    throw new Error(Array.isArray(message) ? message.join(' — ') : message);
  }
  return res.data;
}

function useInvalidate(...keys: string[]) {
  const queryClient = useQueryClient();
  return () =>
    Promise.all(keys.map((key) => queryClient.invalidateQueries({ queryKey: [key] })));
}

// ————— Tiers —————

export function usePartners(enabled = true) {
  return useQuery({
    queryKey: ['partners'],
    enabled,
    queryFn: async () => (await unwrap(await api.GET('/partners'))).partners,
  });
}

export function useCreatePartner() {
  const invalidate = useInvalidate('partners');
  return useMutation({
    mutationFn: async (body: PartnerCreate) => unwrap(await api.POST('/partners', { body })),
    onSuccess: invalidate,
  });
}

export function useUpdatePartner() {
  const invalidate = useInvalidate('partners');
  return useMutation({
    mutationFn: async ({ id, ...body }: PartnerUpdate & { id: string }) =>
      unwrap(await api.PATCH('/partners/{id}', { params: { path: { id } }, body })),
    onSuccess: invalidate,
  });
}

// ————— Pièces & stock —————

export function useParts(enabled = true) {
  return useQuery({
    queryKey: ['parts'],
    enabled,
    queryFn: async () => (await unwrap(await api.GET('/parts'))).parts,
  });
}

export function usePart(id: string | undefined) {
  return useQuery({
    queryKey: ['parts', id],
    enabled: !!id,
    queryFn: async () =>
      unwrap(await api.GET('/parts/{id}', { params: { path: { id: id! } } })),
  });
}

export function useCreatePart() {
  const invalidate = useInvalidate('parts');
  return useMutation({
    mutationFn: async (body: PartCreate) => unwrap(await api.POST('/parts', { body })),
    onSuccess: invalidate,
  });
}

export function useUpdatePart(id: string) {
  const invalidate = useInvalidate('parts');
  return useMutation({
    mutationFn: async (body: PartUpdate) =>
      unwrap(await api.PATCH('/parts/{id}', { params: { path: { id } }, body })),
    onSuccess: invalidate,
  });
}

export function useAddStockMovement(id: string) {
  const invalidate = useInvalidate('parts');
  return useMutation({
    mutationFn: async (body: StockMovementCreate) =>
      unwrap(await api.POST('/parts/{id}/movements', { params: { path: { id } }, body })),
    onSuccess: invalidate,
  });
}

// ————— Bons de commande —————

export function usePurchaseOrders(enabled = true) {
  return useQuery({
    queryKey: ['purchase-orders'],
    enabled,
    queryFn: async () =>
      (await unwrap(await api.GET('/purchase-orders'))).purchaseOrders,
  });
}

export function useCreatePurchaseOrder() {
  const invalidate = useInvalidate('purchase-orders');
  return useMutation({
    mutationFn: async (body: PurchaseOrderCreate) =>
      unwrap(await api.POST('/purchase-orders', { body })),
    onSuccess: invalidate,
  });
}

export function useTransitionPurchaseOrder() {
  const invalidate = useInvalidate('purchase-orders', 'parts');
  return useMutation({
    mutationFn: async ({ id, ...body }: PurchaseOrderTransition & { id: string }) =>
      unwrap(
        await api.POST('/purchase-orders/{id}/transition', { params: { path: { id } }, body }),
      ),
    onSuccess: invalidate,
  });
}

// ————— Coûts sur OT —————

export function useConsumePart(workOrderId: string) {
  const invalidate = useInvalidate('work-orders', 'parts');
  return useMutation({
    mutationFn: async (body: ConsumePart) =>
      unwrap(
        await api.POST('/work-orders/{id}/consume-part', {
          params: { path: { id: workOrderId } },
          body,
        }),
      ),
    onSuccess: invalidate,
  });
}

export function useAddLaborTime(workOrderId: string) {
  const invalidate = useInvalidate('work-orders');
  return useMutation({
    mutationFn: async (body: LaborTimeCreate) =>
      unwrap(
        await api.POST('/work-orders/{id}/labor', {
          params: { path: { id: workOrderId } },
          body,
        }),
      ),
    onSuccess: invalidate,
  });
}

// ————— Bibliothèque —————

export function useDocuments(filtres: { assetId?: string; workOrderId?: string; kind?: string } = {}) {
  return useQuery({
    queryKey: ['documents', filtres],
    queryFn: async () =>
      (
        await unwrap(
          await api.GET('/documents', { params: { query: filtres } }),
        )
      ).documents,
  });
}

export function useUploadDocument() {
  const invalidate = useInvalidate('documents');
  return useMutation({
    mutationFn: async (input: {
      file: File;
      kind: string;
      assetId?: string;
      workOrderId?: string;
    }) => {
      const form = new FormData();
      form.append('file', input.file);
      form.append('kind', input.kind);
      if (input.assetId) form.append('assetId', input.assetId);
      if (input.workOrderId) form.append('workOrderId', input.workOrderId);
      const res = await fetch('/api/documents', {
        method: 'POST',
        headers: { Authorization: `Bearer ${getToken()}` },
        body: form,
      });
      if (!res.ok) {
        const corps = (await res.json().catch(() => null)) as { message?: string } | null;
        throw new Error(corps?.message ?? `Téléversement refusé (${res.status})`);
      }
      return res.json();
    },
    onSuccess: invalidate,
  });
}

export function useDeleteDocument() {
  const invalidate = useInvalidate('documents');
  return useMutation({
    mutationFn: async (id: string) => {
      const res = await api.DELETE('/documents/{id}', { params: { path: { id } } });
      if (res.response.status >= 400) throw new Error('Suppression impossible');
    },
    onSuccess: invalidate,
  });
}

/** Récupération authentifiée du fichier (un lien nu ne porte pas le jeton). */
export async function blobDocument(id: string): Promise<Blob> {
  const res = await fetch(`/api/documents/${id}/download`, {
    headers: { Authorization: `Bearer ${getToken()}` },
  });
  if (!res.ok) throw new Error('Téléchargement impossible');
  return res.blob();
}

export async function telechargerDocument(id: string, fileName: string): Promise<void> {
  const blob = await blobDocument(id);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = fileName;
  a.click();
  URL.revokeObjectURL(url);
}

/** Ouverture dans un nouvel onglet (PDF et images : le navigateur les affiche). */
export async function ouvrirDocument(id: string): Promise<void> {
  // Onglet ouvert dans le geste utilisateur, sinon les bloqueurs de pop-up
  // refusent un window.open survenant après le fetch.
  const onglet = window.open('', '_blank');
  try {
    const blob = await blobDocument(id);
    const url = URL.createObjectURL(blob);
    if (onglet) onglet.location.href = url;
    else window.open(url, '_blank');
  } catch (e) {
    onglet?.close();
    throw e;
  }
}

// ————— Analytics —————

export function useAnalyticsSummary(months: 3 | 6 | 12 = 12, enabled = true) {
  return useQuery({
    queryKey: ['analytics', months],
    enabled,
    queryFn: async () =>
      unwrap(
        await api.GET('/analytics/summary', {
          params: { query: { months: String(months) } },
        }),
      ),
  });
}
