// Multipart upload helper — Task 10. lib/api.ts's request() always sends
// application/json; the setup wizard's two QuickBooks uploads (Account
// Listing, Journal) need a "file" field posted as multipart/form-data
// instead (server/src/imports.ts's receiveXlsxUpload() reads
// request.file(), expecting the part's fieldname to be exactly "file").
// Kept as a separate function rather than a request() option: a FormData
// body must NOT get a manual Content-Type header — the browser sets the
// multipart boundary itself — which is the opposite of every other call
// this app makes, so sharing one code path would just be a footgun.
//
// Errors are surfaced the same way lib/api.ts does (ApiError with the
// server's English message), and — since the wizard needs to render the
// structured detail some 422s carry (conflicts/unknownTypes/duplicateNames
// on chart import, dirtyYears on post) — ApiError.body carries the full
// parsed JSON error response alongside the message.
//
// Task 12 (year-end close) is the first caller to actually pass
// `extraFields` — the "year" text field that rides alongside the Trial
// Balance file on /api/c/close/compare and /api/c/close/lock. Per
// server/src/close.ts's file header, @fastify/multipart only guarantees a
// text field is populated on `part.fields` by the time `request.file()`
// resolves if that field appears BEFORE the file part in the multipart
// stream — so extraFields are appended to the FormData first, and the file
// last, not the other way around.

import { ApiError } from "../lib/api";

function extractErrorMessage(status: number, data: unknown): string {
  if (data && typeof data === "object" && "error" in data) {
    const err = (data as { error?: unknown }).error;
    if (typeof err === "string" && err.trim().length > 0) return err;
  }
  return `Something went wrong talking to the server (HTTP ${status}).`;
}

/**
 * POSTs `file` under a "file" form field to `path`, scoped to `companyId`
 * via X-Company-Id (every /api/c/* upload route requires it). `extraFields`
 * are appended as plain string form fields alongside the file, if any
 * upload route ever needs one (none currently do).
 */
export async function apiUpload<T>(
  path: string,
  file: File,
  companyId: string,
  extraFields?: Record<string, string>,
): Promise<T> {
  const form = new FormData();
  if (extraFields) {
    for (const [key, value] of Object.entries(extraFields)) form.append(key, value);
  }
  form.append("file", file, file.name);

  let res: Response;
  try {
    res = await fetch(path, {
      method: "POST",
      headers: { "X-Company-Id": companyId },
      body: form,
    });
  } catch {
    throw new ApiError("Could not reach the server. Check that it's running and try again.");
  }

  const text = await res.text();
  let data: unknown = null;
  if (text) {
    try {
      data = JSON.parse(text);
    } catch {
      data = null;
    }
  }

  if (!res.ok) {
    throw new ApiError(extractErrorMessage(res.status, data), data);
  }
  return data as T;
}
