// Cliente HTTP da SAR Web.
//
// Responsabilidades:
//   - Encapsular fetch com base URL relativa (proxy Vite em dev, mesmo origin em prod).
//   - Parsear RFC 9457 application/problem+json em ApiError estruturado.
//   - NÃO faz validação Zod — isso é responsabilidade do caller (useQuery + Schema.parse).
//
// CODING-RULES §05: 422 = validação Zod; 4xx outros = erros de domínio; 5xx = retry pelo
// QueryClient (até 2x). O ApiError carrega tudo que o caller precisa pra decidir.

import { authStore } from './auth-store';

const PROBLEM_CONTENT_TYPE = 'application/problem+json';

// Prefixo canônico: proxy Vite (/api → :3000) + versão da API.
// Em produção o Nginx faz o mesmo roteamento pelo mesmo prefixo.
const API_BASE = '/api/v1';

export interface ProblemDetails {
  type: string;
  title: string;
  status: number;
  detail?: string;
  instance?: string;
  requestId?: string;
  errors?: ReadonlyArray<{ path: string; message: string; code?: string }>;
}

export class ApiError extends Error {
  readonly status: number;
  readonly problem: ProblemDetails;

  constructor(problem: ProblemDetails) {
    super(problem.detail ?? problem.title);
    this.name = 'ApiError';
    this.status = problem.status;
    this.problem = problem;
  }
}

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

export async function apiFetch(path: string, options: RequestOptions = {}): Promise<unknown> {
  const { body, headers, ...rest } = options;

  const token = authStore.get();

  const init: RequestInit = {
    ...rest,
    headers: {
      Accept: 'application/json',
      ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...headers,
    },
    ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
  };

  const response = await fetch(`${API_BASE}${path}`, init);

  if (!response.ok) {
    throw await toApiError(response);
  }

  // 204 No Content
  if (response.status === 204) return undefined;

  return response.json();
}

async function toApiError(response: Response): Promise<ApiError> {
  const contentType = response.headers.get('Content-Type') ?? '';

  if (contentType.includes(PROBLEM_CONTENT_TYPE) || contentType.includes('application/json')) {
    try {
      const body = (await response.json()) as ProblemDetails;
      return new ApiError({
        type: body.type ?? 'about:blank',
        title: body.title ?? response.statusText,
        status: body.status ?? response.status,
        detail: body.detail,
        instance: body.instance,
        requestId: body.requestId,
        errors: body.errors,
      });
    } catch {
      // fall through
    }
  }

  return new ApiError({
    type: 'about:blank',
    title: response.statusText || 'Request failed',
    status: response.status,
  });
}
