import Constants from 'expo-constants';
import { supabase } from './supabase';
import { getDeviceInfo } from './deviceId';
import { useDeviceLimitStore } from '../stores/deviceLimit';

const apiUrl = Constants.expoConfig?.extra?.apiUrl as string;

type FetchOptions = Omit<RequestInit, 'body'> & {
  body?: any;
  /** Set true on bootstrap calls (device register) to skip x-device-id injection */
  skipDeviceHeader?: boolean;
};

let cachedDeviceHeaders: Record<string, string> | null = null;

async function getDeviceHeaders(): Promise<Record<string, string>> {
  if (cachedDeviceHeaders) return cachedDeviceHeaders;
  const info = await getDeviceInfo().catch(() => null);
  if (!info) return {};
  cachedDeviceHeaders = {
    'x-device-id': info.deviceId,
    'x-platform': info.platform,
    'x-device-name': encodeURIComponent(info.name),
    'x-device-model': encodeURIComponent(info.model),
    'x-device-os': encodeURIComponent(info.osVersion),
  };
  return cachedDeviceHeaders;
}

/**
 * Wrapper für Backend-API-Calls mit automatischem Auth-Token.
 * Pendant zum Nuxt-`useSafeFetch` aus apps/rebreak/.
 *
 * Backend antwortet mit { success, data, status } — wir entpacken `data`.
 */
export async function apiFetch<T = any>(
  path: string,
  options: FetchOptions = {}
): Promise<T> {
  const session = (await supabase.auth.getSession()).data.session;

  const { skipDeviceHeader, ...fetchOptions } = options;

  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
    ...(fetchOptions.headers as Record<string, string>),
  };

  if (session?.access_token) {
    headers.Authorization = `Bearer ${session.access_token}`;
  }

  if (!skipDeviceHeader) {
    const deviceHeaders = await getDeviceHeaders();
    Object.assign(headers, deviceHeaders);
  }

  const res = await fetch(`${apiUrl}${path}`, {
    ...fetchOptions,
    headers,
    body: fetchOptions.body ? JSON.stringify(fetchOptions.body) : undefined,
  });

  if (!res.ok) {
    const text = await res.text();

    if (res.status === 403) {
      try {
        const parsed = JSON.parse(text);
        if (
          parsed?.statusMessage === 'device_limit_reached' ||
          parsed?.data?.error === 'device_limit_reached'
        ) {
          const { devices, max, plan } = parsed.data;
          useDeviceLimitStore.getState().show(devices ?? [], max ?? 0, plan ?? 'free');
        }
      } catch {}
    }

    // Throw a human-readable Error.message. Backend `createError({ data: { error, message } })`
    // serialises into { error: true, statusCode, statusMessage, message?, data: {…} }.
    // Caller code only ever displays `e.message`, so collapse the prettiest field into
    // it; stash the rest on the Error so callers that want to switch on error_code
    // (e.g. WEB_LIMIT_REACHED, ALREADY_GLOBAL) can still inspect `(e as any).code`.
    let humanMessage = `API ${res.status}`;
    let errorCode: string | undefined;
    let errorData: any;
    try {
      const parsed = JSON.parse(text);
      errorData = parsed?.data ?? parsed;
      errorCode = errorData?.error ?? parsed?.statusMessage;
      humanMessage =
        errorData?.message ??
        parsed?.message ??
        parsed?.statusMessage ??
        humanMessage;
    } catch {
      if (text) humanMessage = text;
    }
    const err = new Error(humanMessage);
    (err as any).status = res.status;
    (err as any).code = errorCode;
    (err as any).data = errorData;
    throw err;
  }

  const json = await res.json();

  // Unwrap { success, data, status } — siehe useSafeFetch-Pattern in der Vue-App
  if (json && typeof json === 'object' && 'success' in json && 'data' in json) {
    return json.data as T;
  }
  return json as T;
}
