/* API layer — auth, fetch, hooks, helpers */

const API_BASE = '/api/v1';

// ── Token cache ───────────────────────────────────────────────
let _token = null;
let _tokenExp = 0;

async function _ensureToken() {
  if (_token && Date.now() / 1000 < _tokenExp - 120) return _token;
  const initData = window.Telegram?.WebApp?.initData;
  if (!initData) throw new Error('Telegram initData недоступен');
  const resp = await fetch(`${API_BASE}/auth/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ init_data: initData }),
  });
  if (!resp.ok) {
    const err = new Error(`Auth failed: ${resp.status}`);
    err.status = resp.status;
    throw err;
  }
  const d = await resp.json();
  _token = d.token;
  _tokenExp = d.expires_at;
  return _token;
}

async function apiFetch(path, opts = {}) {
  const token = await _ensureToken();
  const resp = await fetch(`${API_BASE}${path}`, {
    ...opts,
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
      ...(opts.headers || {}),
    },
  });
  if (!resp.ok) {
    let body;
    try { body = await resp.json(); } catch { body = { error: resp.statusText }; }
    const err = new Error(body.error || body.detail || 'api_error');
    err.status = resp.status;
    err.body = body;
    throw err;
  }
  if (resp.status === 204) return null;
  return resp.json();
}

// Multipart form upload (for file inject)
async function apiUpload(path, formData) {
  const token = await _ensureToken();
  const resp = await fetch(`${API_BASE}${path}`, {
    method: 'POST',
    headers: { 'Authorization': `Bearer ${token}` },
    body: formData,
  });
  if (!resp.ok) {
    let body;
    try { body = await resp.json(); } catch { body = { error: resp.statusText }; }
    throw Object.assign(new Error(body.error || 'upload_error'), { status: resp.status });
  }
  return resp.json();
}

// ── API calls ─────────────────────────────────────────────────
const API = {
  status:     () => apiFetch('/status'),
  inbox:      (project) => apiFetch(`/inbox${project ? '?project=' + encodeURIComponent(project) : ''}`),
  notification: (id) => apiFetch(`/inbox/${id}`),
  replyNotif: (id, response) => apiFetch(`/inbox/${id}/reply`, { method: 'POST', body: JSON.stringify({ response }) }),

  projects:       () => apiFetch('/projects'),
  project:        (name) => apiFetch(`/projects/${name}`),
  pauseProject:   (name) => apiFetch(`/projects/${name}/pause`, { method: 'POST' }),
  resumeProject:  (name) => apiFetch(`/projects/${name}/resume`, { method: 'POST' }),
  approveArc:     (name, arcId) => apiFetch(`/projects/${name}/arcs/${arcId}/approve`, { method: 'POST' }),
  rejectArc:      (name, arcId, reason) => apiFetch(`/projects/${name}/arcs/${arcId}/reject`, { method: 'POST', body: JSON.stringify({ reason }) }),

  chatStart:    (name) => apiFetch(`/projects/${name}/chat/start`, { method: 'POST' }),
  chatStatus:   (name) => apiFetch(`/projects/${name}/chat/status`),
  chatMessage:  (name, text) => apiFetch(`/projects/${name}/chat/message`, { method: 'POST', body: JSON.stringify({ text }) }),
  chatFinalize: (name) => apiFetch(`/projects/${name}/chat/finalize`, { method: 'POST' }),
  chatRelease:  (name) => apiFetch(`/projects/${name}/chat`, { method: 'DELETE' }),

  settingsMode:    () => apiFetch('/settings/mode'),
  setSettingsMode: (mode) => apiFetch('/settings/mode', { method: 'POST', body: JSON.stringify({ mode }) }),

  injectText: (name, text) => apiFetch(`/projects/${name}/inject/instruction`, { method: 'POST', body: JSON.stringify({ text }) }),
  injectFile: (name, formData) => apiUpload(`/projects/${name}/inject/file`, formData),
};

// ── Helpers ───────────────────────────────────────────────────
function relTime(ts) {
  if (!ts) return '';
  const diff = Math.floor(Date.now() / 1000 - ts);
  if (diff < 60) return `${diff}s`;
  if (diff < 3600) return `${Math.floor(diff / 60)}m`;
  if (diff < 86400) return `${Math.floor(diff / 3600)}h`;
  return `${Math.floor(diff / 86400)}d`;
}

const _NOTIF_ICON_MAP = {
  phase_plan_review:            { kind: 'phase',  label: 'PR' },
  consilium_iteration_review:   { kind: 'cons',   label: 'C2' },
  todo_escalation:              { kind: 'todo',   label: 'T!' },
  todo_unsure:                  { kind: 'todo',   label: 'T?' },
  ultimate_replacement_confirm: { kind: 'system', label: 'U!' },
  subproject_merge_approval:    { kind: 'merge',  label: 'SP' },
  arc_closure:                  { kind: 'arc',    label: 'AC' },
  linter_degraded:              { kind: 'system', label: 'M2' },
};

function notifIcon(type) {
  return _NOTIF_ICON_MAP[type] || { kind: 'system', label: '?' };
}

// ── Loading / error primitives ────────────────────────────────
function LoadingSpinner() {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 40, color: 'var(--tg-theme-hint-color)' }}>
      <svg width="28" height="28" viewBox="0 0 28 28" style={{ animation: 'spin 0.8s linear infinite' }}>
        <circle cx="14" cy="14" r="11" fill="none" stroke="currentColor" strokeWidth="2.5" strokeDasharray="50 20" strokeLinecap="round" />
      </svg>
    </div>
  );
}

function ErrorRetry({ message, onRetry }) {
  return (
    <div style={{ padding: '20px 16px', textAlign: 'center', color: 'var(--tg-theme-hint-color)', fontSize: 14 }}>
      <div style={{ marginBottom: 12, color: 'var(--ag-fail)' }}>{message || 'Ошибка загрузки'}</div>
      {onRetry && <button className="btn ghost sm" onClick={onRetry}>Повторить</button>}
    </div>
  );
}

// ── React data hook ───────────────────────────────────────────
function useApi(fetcher, deps) {
  const [state, setState] = React.useState({ data: null, loading: true, error: null });
  const reload = React.useCallback(() => {
    setState(s => ({ ...s, loading: true, error: null }));
    fetcher()
      .then(data => setState({ data, loading: false, error: null }))
      .catch(e => setState({ data: null, loading: false, error: e.message || 'Ошибка' }));
  }, deps);
  React.useEffect(() => { reload(); }, [reload]);
  return { ...state, reload };
}

// CSS for spinner
if (!document.getElementById('api-spinner-style')) {
  const s = document.createElement('style');
  s.id = 'api-spinner-style';
  s.textContent = '@keyframes spin { to { transform: rotate(360deg); } }';
  document.head.appendChild(s);
}

Object.assign(window, { API, apiFetch, apiUpload, useApi, relTime, notifIcon, LoadingSpinner, ErrorRetry, ensureToken: _ensureToken });
