import * as store from './store.js';
import * as bus from './bus.js';

const API_BASE = '/api';

export async function api(endpoint, options = {}) {
  const { method = 'GET', body, auth = true } = options;
  const headers = { 'Content-Type': 'application/json' };

  if (auth) {
    const token = store.get('auth.token');
    if (token) headers['Authorization'] = `Bearer ${token}`;
  }

  const config = { method, headers };
  if (body && method !== 'GET') {
    config.body = JSON.stringify(body);
  }

  const url = endpoint.startsWith('http') ? endpoint : `${API_BASE}/${endpoint}`;
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), options.timeout || 10000);
  config.signal = controller.signal;
  let res;
  try {
    res = await fetch(url, config);
  } finally {
    clearTimeout(timeout);
  }
  const contentType = res.headers.get('content-type') || '';
  if (!contentType.includes('application/json')) {
    const snippet = (await res.text()).slice(0, 120);
    throw new ApiError(
      `Expected JSON but got ${contentType || 'unknown content-type'} (status ${res.status}): ${snippet}`,
      res.status || 502
    );
  }
  const text = await res.text();
  let data;
  try {
    data = JSON.parse(text);
  } catch (e) {
    throw new ApiError(
      `Server returned malformed JSON (status ${res.status}): ${text.slice(0, 120)}`,
      res.status || 502
    );
  }

  if (!res.ok) {
    if (res.status === 401 && !options._retried) {
      const refreshed = await refreshToken();
      if (refreshed) {
        const retryRes = await api(endpoint, { ...options, _retried: true });
        return retryRes;
      }
      bus.emit('auth:expired');
    } else if (res.status === 401 && options._retried) {
      bus.emit('auth:expired');
    }
    throw new ApiError(data.error || 'Request failed', res.status);
  }

  return data;
}

export async function get(endpoint, params = {}) {
  const qs = Object.entries(params)
    .filter(([, v]) => v != null)
    .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
    .join('&');
  const url = qs ? `${endpoint}?${qs}` : endpoint;
  return api(url);
}

export async function post(endpoint, body) {
  return api(endpoint, { method: 'POST', body });
}

export async function patch(endpoint, body) {
  return api(endpoint, { method: 'PATCH', body });
}

export async function del(endpoint, body) {
  return api(endpoint, { method: 'DELETE', body });
}

let refreshPromise = null;
async function refreshToken() {
  if (refreshPromise) return refreshPromise;
  refreshPromise = doRefresh();
  try { return await refreshPromise; } finally { refreshPromise = null; }
}

async function doRefresh() {
  const refreshToken = store.get('auth.refreshToken');
  if (!refreshToken) return false;
  try {
    const res = await fetch(`${API_BASE}/auth.php`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ action: 'refresh', refresh_token: refreshToken })
    });
    const ct = res.headers.get('content-type') || '';
    if (!ct.includes('application/json')) return false;
    const data = await res.json();
    if (data.access_token) {
      store.set('auth.token', data.access_token);
      store.set('auth.refreshToken', data.refresh_token);
      return true;
    }
  } catch (e) {}
  return false;
}

class ApiError extends Error {
  constructor(message, status) {
    super(message);
    this.status = status;
  }
}
