// api.jsx — Shared API client + React hooks
//
// Loaded by BOTH index.html (client app) and admin.html (admin console).
// All network calls go through this module so auth + base URL are configured once.
//
// Configure the API base URL by setting window.__DL_API_BASE before this file loads,
// or via the <meta name="dl-api-base" content="..."> tag (preferred).

(function setupApiBase() {
  if (window.__DL_API_BASE) return;
  const meta = document.querySelector('meta[name="dl-api-base"]');
  if (meta?.content) window.__DL_API_BASE = meta.content;
  else window.__DL_API_BASE = ''; // same-origin fallback
})();

// ─── Shared client data shell — populated by useClientStore in app.jsx ───
window.DL_DATA = window.DL_DATA || {
  client: {
    first: '', name: '', biz: '', code: '', avatarInitials: '',
    memberSince: '', plan: '', bankBrand: '', bankLast4: '',
    bizAddress: '', email: '', phone: '',
  },
  totals: {
    enrolled: 0, settled: 0, saved: 0,
    nextPayment: 0, nextDate: '—',
    planMonths: 0, planMonthsDone: 0, planProgress: 0,
  },
  lenders: [],
  activity: [],
  payments: [],
  documents: [],
  files: [],
  specialist: null,
};

const API_BASE = (window.__DL_API_BASE || '').replace(/\/$/, '');
const TOKEN_KEY = 'dl_session_token_v1';
const USER_KEY  = 'dl_session_user_v1';

// ─── Token + user storage ────────────────────────────────────
const Auth = {
  getToken() { try { return localStorage.getItem(TOKEN_KEY); } catch { return null; } },
  setToken(t) { try { t ? localStorage.setItem(TOKEN_KEY, t) : localStorage.removeItem(TOKEN_KEY); } catch {} },
  getUser() {
    try { const s = localStorage.getItem(USER_KEY); return s ? JSON.parse(s) : null; } catch { return null; }
  },
  setUser(u) { try { u ? localStorage.setItem(USER_KEY, JSON.stringify(u)) : localStorage.removeItem(USER_KEY); } catch {} },
  clear() { this.setToken(null); this.setUser(null); },
};

// ─── fetch wrapper ───────────────────────────────────────────
async function request(path, opts = {}) {
  const headers = new Headers(opts.headers || {});
  const tok = Auth.getToken();
  if (tok) headers.set('Authorization', 'Bearer ' + tok);
  if (opts.body && !(opts.body instanceof FormData) && !headers.has('Content-Type')) {
    headers.set('Content-Type', 'application/json');
  }
  const body = opts.body && !(opts.body instanceof FormData) && typeof opts.body !== 'string'
    ? JSON.stringify(opts.body)
    : opts.body;

  let res;
  try {
    res = await fetch(API_BASE + path, { ...opts, headers, body, credentials: 'include' });
  } catch (e) {
    throw new ApiError('Network error. Check your connection.', 0, { network: true });
  }

  if (res.status === 204) return null;

  let payload;
  const ct = res.headers.get('Content-Type') || '';
  if (ct.includes('application/json')) {
    try { payload = await res.json(); } catch { payload = null; }
  } else if (opts.expectBlob) {
    return await res.blob();
  } else {
    payload = await res.text();
  }

  if (!res.ok) {
    if (res.status === 401 && Auth.getToken()) {
      // Session expired — wipe and force re-login
      Auth.clear();
      window.dispatchEvent(new CustomEvent('dl-session-expired'));
    }
    const msg = (payload && payload.error) || `Request failed (${res.status})`;
    throw new ApiError(msg, res.status, payload || {});
  }
  return payload;
}

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

// ─── API surface ─────────────────────────────────────────────
const api = {
  // Auth
  async login(email, password) {
    return await request('/api/auth/login', { method: 'POST', body: { email, password } });
  },
  async verifyOtp(otp_id, code) {
    const res = await request('/api/auth/verify-otp', { method: 'POST', body: { otp_id, code } });
    if (res?.token) {
      Auth.setToken(res.token);
      Auth.setUser(res.user);
    }
    return res;
  },
  async logout() {
    try { await request('/api/auth/logout', { method: 'POST' }); } catch {}
    Auth.clear();
  },
  async me() {
    const r = await request('/api/auth/me');
    if (r?.user) Auth.setUser(r.user);
    return r;
  },
  async changePassword(current, next) {
    return await request('/api/auth/change-password', { method: 'POST', body: { current, next } });
  },
  async requestReset(email) {
    return await request('/api/auth/request-reset', { method: 'POST', body: { email } });
  },
  async resetPassword(email, code, password) {
    return await request('/api/auth/reset-password', { method: 'POST', body: { email, code, password } });
  },
  async activateByEmail(email, code, password) {
    return await request('/api/auth/activate-by-email', { method: 'POST', body: { email, code, password } });
  },

  // Client (self)
  client: {
    me:        () => request('/api/me'),
    lenders:   () => request('/api/me/lenders'),
    activity:  () => request('/api/me/activity'),
    payments:  () => request('/api/me/payments'),
    documents: () => request('/api/me/documents'),
    downloadDoc(id) {
      return request(`/api/me/documents/${encodeURIComponent(id)}/download`, { expectBlob: true });
    },
  },

  // Admin (and referral, scoped on the backend)
  admin: {
    clients(filters = {}) {
      const qs = new URLSearchParams();
      if (filters.status) qs.set('status', filters.status);
      if (filters.q) qs.set('q', filters.q);
      const s = qs.toString();
      return request('/api/admin/clients' + (s ? '?' + s : ''));
    },
    client:        (id) => request(`/api/admin/clients/${encodeURIComponent(id)}`),
    createClient:  (body) => request('/api/admin/clients', { method: 'POST', body }),
    updateClient:  (id, body) => request(`/api/admin/clients/${encodeURIComponent(id)}`, { method: 'PATCH', body }),

    lenders:       (clientId) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/lenders`),
    createLender:  (clientId, body) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/lenders`, { method: 'POST', body }),
    updateLender:  (id, body) => request(`/api/admin/lenders/${encodeURIComponent(id)}`, { method: 'PATCH', body }),

    events:        (clientId) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/events`),
    createEvent:   (clientId, body) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/events`, { method: 'POST', body }),

    payments:      (clientId) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/payments`),
    createPayment: (clientId, body) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/payments`, { method: 'POST', body }),
    updatePayment: (id, body) => request(`/api/admin/payments/${encodeURIComponent(id)}`, { method: 'PATCH', body }),

    documents:     (clientId) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/documents`),
    uploadDocument(clientId, file, { folder, tag, visibility } = {}) {
      const fd = new FormData();
      fd.append('file', file, file.name);
      if (folder) fd.append('folder', folder);
      if (tag) fd.append('tag', tag);
      if (visibility) fd.append('visibility', visibility);
      return request(`/api/admin/clients/${encodeURIComponent(clientId)}/documents`, { method: 'POST', body: fd });
    },
    deleteDocument: (id) => request(`/api/admin/documents/${encodeURIComponent(id)}`, { method: 'DELETE' }),
    downloadDocument(id) {
      return request(`/api/admin/documents/${encodeURIComponent(id)}/download`, { expectBlob: true });
    },

    audit:     (clientId) => request(`/api/admin/clients/${encodeURIComponent(clientId)}/audit`),
    reps:      () => request('/api/admin/reps'),
    referrals: () => request('/api/admin/referrals'),

    // Team management
    team:           () => request('/api/admin/team'),
    inviteAdmin:    (body) => request('/api/admin/team/invite-admin',    { method: 'POST', body }),
    inviteReferral: (body) => request('/api/admin/team/invite-referral', { method: 'POST', body }),
    updateMember:   (id, body) => request(`/api/admin/team/${encodeURIComponent(id)}`,                  { method: 'PATCH', body }),
    disableMember:  (id)       => request(`/api/admin/team/${encodeURIComponent(id)}/disable`,          { method: 'POST' }),
    enableMember:   (id)       => request(`/api/admin/team/${encodeURIComponent(id)}/enable`,           { method: 'POST' }),
    resendInvite:   (id)       => request(`/api/admin/team/${encodeURIComponent(id)}/resend-invite`,    { method: 'POST' }),
  },
};

// ─── React hooks (assumes React loaded already) ──────────────
function useAuth() {
  const [user, setUser] = React.useState(() => Auth.getUser());
  const [authed, setAuthed] = React.useState(() => !!Auth.getToken());

  // Verify token still valid on mount
  React.useEffect(() => {
    if (!authed) return;
    let cancelled = false;
    api.me()
      .then(r => { if (!cancelled && r?.user) { setUser(r.user); } })
      .catch(() => { if (!cancelled) { Auth.clear(); setUser(null); setAuthed(false); } });
    return () => { cancelled = true; };
  }, []);

  // Listen for session-expired events from other tabs
  React.useEffect(() => {
    const onExpire = () => { setUser(null); setAuthed(false); };
    const onStorage = (e) => {
      if (e.key === TOKEN_KEY) {
        const t = Auth.getToken();
        setAuthed(!!t);
        if (!t) setUser(null);
      }
    };
    window.addEventListener('dl-session-expired', onExpire);
    window.addEventListener('storage', onStorage);
    return () => {
      window.removeEventListener('dl-session-expired', onExpire);
      window.removeEventListener('storage', onStorage);
    };
  }, []);

  const signIn = React.useCallback(async (email, password) => {
    return await api.login(email, password); // returns { otp_id }
  }, []);

  const verifyOtp = React.useCallback(async (otpId, code) => {
    const r = await api.verifyOtp(otpId, code);
    if (r?.user) {
      setUser(r.user);
      setAuthed(true);
    }
    return r;
  }, []);

  const signOut = React.useCallback(async () => {
    await api.logout();
    setUser(null);
    setAuthed(false);
  }, []);

  return { user, authed, signIn, verifyOtp, signOut };
}

// Generic fetch hook with loading + error + refetch
function useResource(loader, deps = []) {
  const [state, setState] = React.useState({ data: null, loading: true, error: null });
  const run = React.useCallback(async () => {
    setState(s => ({ ...s, loading: true, error: null }));
    try {
      const data = await loader();
      setState({ data, loading: false, error: null });
    } catch (e) {
      setState({ data: null, loading: false, error: e });
    }
  }, deps);
  React.useEffect(() => { run(); }, [run]);
  return { ...state, refetch: run };
}

Object.assign(window, { api, Auth, ApiError, useAuth, useResource });
