'use client';

import { useUIStore } from '@/stores/ui-store';

export interface ApiFetchOptions extends Omit<RequestInit, 'body'> {
  body?: unknown;
}

/** In-memory cache: slug -> id, populated lazily by `resolvePortIdFromSlug`.
 *  Avoids re-fetching `/api/v1/admin/ports` on every request when the Zustand
 *  store hasn't hydrated yet (fresh browser context, e2e tests, hard reload). */
const slugToIdCache = new Map<string, string>();
/** Dedupe in-flight admin/ports lookups so a stampede of parallel apiFetch
 *  calls (typical on dashboard mount) collapses into a single network round-
 *  trip instead of N. */
let inFlightPortsLookup: Promise<Array<{ id: string; slug: string }> | null> | null = null;

async function resolvePortIdFromSlug(slug: string): Promise<string | null> {
  const cached = slugToIdCache.get(slug);
  if (cached) return cached;
  if (!inFlightPortsLookup) {
    inFlightPortsLookup = (async () => {
      try {
        const res = await fetch('/api/v1/admin/ports', { credentials: 'include' });
        if (!res.ok) return null;
        const body = (await res.json()) as { data?: Array<{ id: string; slug: string }> };
        return body.data ?? null;
      } catch {
        return null;
      }
    })().finally(() => {
      inFlightPortsLookup = null;
    });
  }
  const ports = await inFlightPortsLookup;
  const port = ports?.find((p) => p.slug === slug);
  if (!port) return null;
  slugToIdCache.set(slug, port.id);
  return port.id;
}

/**
 * Client-side fetch wrapper that attaches the `X-Port-Id` header from the
 * UI store to every request. Used by all queryFn/mutationFn callbacks.
 *
 * Falls back to extracting the port slug from `window.location.pathname` and
 * resolving it via `/api/v1/admin/ports` when the Zustand store hasn't been
 * populated yet (fresh page load before `PortProvider`'s effect has fired).
 */
export async function apiFetch<T = unknown>(url: string, opts: ApiFetchOptions = {}): Promise<T> {
  let portId = useUIStore.getState().currentPortId;

  if (!portId && typeof window !== 'undefined') {
    const slug = window.location.pathname.split('/').filter(Boolean)[0];
    if (slug && slug !== 'login' && slug !== 'portal' && slug !== 'api') {
      portId = await resolvePortIdFromSlug(slug);
    }
  }

  const headers = new Headers(opts.headers);
  if (portId) {
    headers.set('X-Port-Id', portId);
  }
  if (opts.body !== undefined && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json');
  }

  const res = await fetch(url, {
    ...opts,
    headers,
    credentials: 'include',
    body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
  });

  if (!res.ok) {
    const error = await res.json().catch(() => ({ error: res.statusText }));
    throw Object.assign(new Error(error.error ?? 'Request failed'), {
      status: res.status,
      code: error.code,
      details: error.details,
    });
  }

  if (res.status === 204) return undefined as T;
  return res.json() as Promise<T>;
}
