
// apiClient.js
// Reusable API client for OZIXSOFT/KOMBAR FERD web apps
// - Centralizes fetch with JSON parsing, error handling, timeouts, and auth token management
// - Works with both pesee_webapp.html and search_combobox.html
//
// Usage (in your HTML):
// <script type="module">
//   import { api } from './apiClient.js';
//   window.api = api; // (optional) expose globally if you call it from inline handlers
// </script>

const DEFAULT_BASE_URL = 'https://serverkmaf.helioho.st/ozixsoftapp/pesees-api';

const TOKEN_KEY = 'ozix_auth_token';          // sessionStorage key for token
const USER_KEY  = 'ozix_current_user';        // sessionStorage key for user payload

// Helper: timeout wrapper for fetch
function withTimeout(promise, ms = 15000) {
  return new Promise((resolve, reject) => {
    const id = setTimeout(() => reject(new Error(`Timeout après ${ms}ms`)), ms);
    promise
      .then((res) => { clearTimeout(id); resolve(res); })
      .catch((err) => { clearTimeout(id); reject(err); });
  });
}

// Build URL with query params
function buildUrl(base, path, query) {
  const url = new URL(path, base.endsWith('/') ? base : base + '/');
  if (query && typeof query === 'object') {
    Object.entries(query).forEach(([k, v]) => {
      if (v !== undefined && v !== null && String(v).trim() !== '') {
        url.searchParams.append(k, v);
      }
    });
  }
  return url.toString();
}

// Detect if a string looks like HTML (common when PHP errors leak as HTML)
function looksLikeHTML(text) {
  const trimmed = (text || '').trim();
  return trimmed.startsWith('<!DOCTYPE') || trimmed.startsWith('<html') || trimmed.startsWith('<');
}

// Token helpers
function getToken() {
  try { return sessionStorage.getItem(TOKEN_KEY); } catch { return null; }
}
function setToken(token) {
  try {
    if (token) sessionStorage.setItem(TOKEN_KEY, token);
    else sessionStorage.removeItem(TOKEN_KEY);
  } catch {}
}
function setUser(user) {
  try {
    if (user) sessionStorage.setItem(USER_KEY, JSON.stringify(user));
    else sessionStorage.removeItem(USER_KEY);
  } catch {}
}
function getUser() {
  try {
    const raw = sessionStorage.getItem(USER_KEY);
    return raw ? JSON.parse(raw) : null;
  } catch { return null; }
}

// Core request
let __unauthorizedHandler = null;

async function request(path, {
  method = 'GET',
  baseUrl = DEFAULT_BASE_URL,
  headers = {},
  body = undefined,
  query = undefined,
  timeout = 15000,
  onUnauthorized = null,   // callback when 401
} = {}) {
  const url = buildUrl(baseUrl, path, query);
  const token = getToken();

  const finalHeaders = {
    'Accept': 'application/json',
    ...headers,
  };

  let finalBody = body;
  if (body && typeof body === 'object' && !(body instanceof FormData)) {
    finalHeaders['Content-Type'] = finalHeaders['Content-Type'] || 'application/json';
    finalBody = JSON.stringify(body);
  }

  if (token) {
    finalHeaders['Authorization'] = `Bearer ${token}`;
  }

  let response;
  try {
    response = await withTimeout(fetch(url, {
      method,
      headers: finalHeaders,
      body: finalBody,
    }), timeout);
  } catch (networkErr) {
    return { ok: false, status: 0, error: `Réseau/timeout: ${networkErr.message}`, url };
  }

  const contentType = response.headers.get('content-type') || '';
  let rawText;
  try {
    rawText = await response.text();
  } catch (readErr) {
    return { ok: false, status: response.status, error: `Lecture réponse échouée: ${readErr.message}`, url };
  }

  if (!rawText || !rawText.trim()) {
    return { ok: false, status: response.status, error: 'Réponse vide du serveur', url };
  }

  if (!contentType.includes('application/json') || looksLikeHTML(rawText)) {
    // Server likely returned HTML error page or wrong content type
    return { ok: false, status: response.status, error: `Réponse non-JSON: ${rawText.substring(0, 300)}`, url };
  }

  let data;
  try {
    data = JSON.parse(rawText);
  } catch (jsonErr) {
    return { ok: false, status: response.status, error: `JSON invalide: ${jsonErr.message}. Extrait: ${rawText.substring(0, 300)}`, url };
  }

  // Handle 401 centrally
  if (response.status === 401) {
    // Clear token/user and notify caller
    setToken(null);
    setUser(null);
    if (typeof onUnauthorized === 'function') {
      try { onUnauthorized(); } catch {}
    }
    if (typeof __unauthorizedHandler === 'function') {
      try { __unauthorizedHandler(); } catch {}
    }
    return { ok: false, status: 401, error: data?.message || 'Non autorisé', data, url };
  }

  // Normalize success shape
  const ok = response.ok && (data?.success !== false);
  return { ok, status: response.status, data, url };
}

// Domain-specific helpers (auth & pesees)
const auth = {
  async login(username, password, { baseUrl = DEFAULT_BASE_URL } = {}) {
    const res = await request('auth', {
      method: 'POST',
      baseUrl,
      body: { action: 'login', username, password },
      onUnauthorized: null,
    });
    if (res.ok && res.data?.data) {
      const { token, ...user } = res.data.data;
      if (token) setToken(token);
      setUser(user);
    }
    return res;
  },
  async logout({ baseUrl = DEFAULT_BASE_URL } = {}) {
    const token = getToken();
    const res = await request('auth', {
      method: 'POST',
      baseUrl,
      body: { action: 'logout', token },
    });
    setToken(null);
    setUser(null);
    return res;
  },
  getToken,
  getUser,
  setToken,
  setUser,
};

const pesees = {
  // GET pesees?action=list&limit=50
  async list(limit = 50, { baseUrl = DEFAULT_BASE_URL } = {}) {
    return request('pesees', {
      baseUrl,
      query: { action: 'list', limit },
    });
  },
  // GET pesees?action=stats
  async stats({ baseUrl = DEFAULT_BASE_URL } = {}) {
    return request('pesees', {
      baseUrl,
      query: { action: 'stats' },
    });
  },
  // GET pesees?action=options
  async options({ baseUrl = DEFAULT_BASE_URL } = {}) {
    return request('pesees', {
      baseUrl,
      query: { action: 'options' },
    });
  },
  // GET pesees?action=ticket&numTicket=XYZ
  async ticket(identifier, { baseUrl = DEFAULT_BASE_URL } = {}) {
    return request('pesees', {
      baseUrl,
      query: { action: 'ticket', numTicket: identifier },
    });
  },
  // Generic search: criteria is an object with possible keys:
  // codePesee, numTicket, client, fournisseur, codeSite, dateDebut, dateFin, produit, provenance, destination, immatriculation, nomPeseur1
  async search(criteria = {}, { baseUrl = DEFAULT_BASE_URL } = {}) {
    const query = { action: 'search' };
    Object.entries(criteria).forEach(([k, v]) => {
      if (v !== undefined && v !== null && String(v).trim() !== '') {
        query[k] = v;
      }
    });
    return request('pesees', { baseUrl, query });
  }
};


export function setUnauthorizedHandler(fn){ __unauthorizedHandler = fn; }
export const api = {
  request,
  auth,
  pesees,
  // Allow consumers to override base URL at runtime if needed
  withBase(baseUrl) {
    return {
      request: (path, opts) => request(path, { baseUrl, ...(opts || {}) }),
      auth: {
        login: (u, p) => auth.login(u, p, { baseUrl }),
        logout: () => auth.logout({ baseUrl }),
        getToken,
        getUser,
        setToken,
        setUser,
      },
      pesees: {
        list: (limit) => pesees.list(limit, { baseUrl }),
        stats: () => pesees.stats({ baseUrl }),
        options: () => pesees.options({ baseUrl }),
        ticket: (id) => pesees.ticket(id, { baseUrl }),
        search: (c) => pesees.search(c, { baseUrl }),
      }
    };
  }
};
