/* global React */
// =======================================================================
// BlazeConnector Admin v3 — Cliente API real + store de datos "live"
// -----------------------------------------------------------------------
// Conecta el UI a la API admin real de BlazeConnector usando X-Admin-Token.
//   · modo "demo" → usa los mocks de data.jsx (sin backend)
//   · modo "live" → datos reales del backend (GET/POST/PUT/DELETE /admin/*)
// El store usa React.useSyncExternalStore (React 18.3.1) para que cualquier
// página se re-renderice cuando cambian los datos. window.TENANTS se mantiene
// sincronizado para los consumidores que aún leen el global (TenantSwitcher,
// routing en app.jsx).
// =======================================================================

const BC_CFG_KEY = 'bc_admin_cfg_v1';

// Snapshot de los mocks originales (data.jsx ya corrió) para el modo demo.
const DEMO_TENANTS = (window.TENANTS || []).slice();
const DEMO_KEYS = (window.API_KEYS || []).slice();
const DEMO_TEMPLATES = (window.TEMPLATES || []).slice();

// ---- Config persistente (localStorage) -------------------------------
function loadCfg() {
  try { return JSON.parse(localStorage.getItem(BC_CFG_KEY) || 'null'); } catch (e) { return null; }
}
function saveCfg(cfg) { try { localStorage.setItem(BC_CFG_KEY, JSON.stringify(cfg)); } catch (e) {} }
function clearCfg() { try { localStorage.removeItem(BC_CFG_KEY); } catch (e) {} }

// ---- External store --------------------------------------------------
const _listeners = new Set();
let _state = {
  mode: 'demo',            // 'demo' | 'live'
  status: 'disconnected',  // 'disconnected' | 'connecting' | 'connected' | 'error'
  demoChosen: false,       // true cuando el usuario eligió explícitamente modo demo
  error: null,
  baseUrl: '',
  tenants: DEMO_TENANTS,
  tenantsLoading: false,
  keysByTenant: {},        // uuid -> { loading, error, data:[] }
  templatesByTenant: {}    // uuid -> { loading, error, data:[] }
};
function getSnapshot() { return _state; }
function subscribe(cb) { _listeners.add(cb); return () => _listeners.delete(cb); }
function setState(patch) {
  _state = Object.assign({}, _state, patch);
  // Mantener el global sincronizado para consumidores no migrados al hook.
  if (patch.tenants) window.TENANTS = patch.tenants;
  _listeners.forEach((l) => l());
}
function useBC() { return React.useSyncExternalStore(subscribe, getSnapshot); }

// ---- HTTP helper -----------------------------------------------------
async function req(method, path, body) {
  const cfg = loadCfg();
  if (!cfg) throw new Error('Sin configuración de conexión');
  const res = await fetch(cfg.baseUrl + path, {
    method,
    headers: Object.assign(
      { 'X-Admin-Token': cfg.token },
      body ? { 'Content-Type': 'application/json' } : {}
    ),
    body: body ? JSON.stringify(body) : undefined
  });
  let json = null;
  try { json = await res.json(); } catch (e) { /* respuesta sin body */ }
  if (!res.ok || (json && json.error)) {
    const msg = (json && (json.message || json.code)) || ('HTTP ' + res.status);
    const err = new Error(msg);
    err.status = res.status;
    throw err;
  }
  return json || {};
}

// ---- Adapters: shape real del backend → shape que esperan las páginas -
function slugify(s) {
  return (s || '').toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '')
    .replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
}

// modules{} (bools del backend) → canales / billing / pagos del UI
const MOD_CHANNEL = { wacloud: 'whatsapp', telegram: 'telegram', chatwoot: 'chatwoot' };
const MOD_BILLING = ['mikrowisp', 'wisphub', 'domiisp', 'oficable', 'smartolt', 'voltage'];
const MOD_PAGOS = ['cardnet', 'azul', 'paypal', 'cybersource'];
const PLAN_LABEL = { free: 'Starter', pro: 'Pro', enterprise: 'Enterprise' };

function adaptTenant(t) {
  const mods = t.modules || {};
  const activeMods = Object.keys(mods).filter((k) => mods[k]);
  return {
    id: t.uuid,            // routing y avatares usan .id → usamos el uuid real
    uuid: t.uuid,
    name: t.name,
    slug: slugify(t.name),
    plan: PLAN_LABEL[t.plan] || (t.plan ? t.plan[0].toUpperCase() + t.plan.slice(1) : '—'),
    status: t.status,
    modules: activeMods,   // array → .length funciona en la lista
    modulesRaw: mods,      // objeto del backend → para la pestaña Módulos
    region: '—',           // la API admin no expone región
    channels: Object.keys(MOD_CHANNEL).filter((k) => mods[k]).map((k) => MOD_CHANNEL[k]),
    billing: MOD_BILLING.find((k) => mods[k]) || null,
    pagos: MOD_PAGOS.filter((k) => mods[k]),
    // Métricas no expuestas por la API admin → 0/—; honestas, no inventadas.
    subs: 0, msgs24h: 0, errorRate: 0,
    lastActive: t.updated_at || t.created_at,
    createdAt: t.created_at,
    _live: true
  };
}

// La struct apikey.APIKey se serializa en snake_case; HashedKey lleva json:"-"
// (jamás llega al cliente). Tolera PascalCase por compatibilidad con backends viejos.
function adaptKey(k, tenantName) {
  const id = k.id || k.ID;
  const tenantId = k.tenant_id || k.TenantID;
  return {
    id,
    name: k.name || k.Name,
    tenant: tenantId,
    tenantName: tenantName || tenantId,
    scopes: k.scopes || k.Scopes || [],
    status: (k.status || k.Status || '').toLowerCase(),
    createdAt: k.created_at || k.CreatedAt,
    lastUsed: k.last_used_at || k.LastUsedAt || k.created_at || k.CreatedAt,
    lastIp: '—',
    preview: 'bk_' + (k.prefix || k.Prefix || '????????') + '_••••••••'
  };
}

function adaptTemplate(t) {
  // Shape defensivo: el registry puede variar de claves.
  return {
    name: t.name || t.Name || '(sin nombre)',
    category: (t.category || t.Category || 'UTILITY').toUpperCase(),
    lang: t.language || t.lang || t.Language || 'es',
    status: (t.status || t.Status || 'PENDING').toUpperCase()
  };
}

// ---- Cliente público -------------------------------------------------
const BC = {
  isConfigured() { return !!loadCfg(); },
  getConfig() { return loadCfg(); },
  setConfig(baseUrl, token) { saveCfg({ baseUrl: (baseUrl || '').replace(/\/+$/, ''), token }); },

  // Prueba de conexión SIN persistir (para el botón "Probar").
  async test(baseUrl, token) {
    const res = await fetch((baseUrl || '').replace(/\/+$/, '') + '/admin/tenants?limit=1', {
      headers: { 'X-Admin-Token': token }
    });
    if (!res.ok) {
      const e = new Error(res.status === 401 || res.status === 403 ? 'Token admin inválido' : 'HTTP ' + res.status);
      e.status = res.status;
      throw e;
    }
    const j = await res.json();
    return Array.isArray(j.data) ? j.data.length : 0;
  },

  async connect(baseUrl, token) {
    this.setConfig(baseUrl, token);
    setState({ mode: 'live', status: 'connecting', error: null, baseUrl: loadCfg().baseUrl });
    try {
      await this.loadTenants();
      setState({ status: 'connected' });
    } catch (e) {
      setState({ status: 'error', error: e.message });
      throw e;
    }
  },

  // Reconecta usando config persistida (al recargar la página).
  async reconnect() {
    const cfg = loadCfg();
    if (!cfg) return false;
    setState({ mode: 'live', status: 'connecting', baseUrl: cfg.baseUrl, error: null });
    try { await this.loadTenants(); setState({ status: 'connected' }); return true; }
    catch (e) { setState({ status: 'error', error: e.message }); return false; }
  },

  // Usuario elige modo demo → oculta el gate, usa mocks.
  demo() {
    setState({
      mode: 'demo', status: 'disconnected', demoChosen: true, error: null,
      tenants: DEMO_TENANTS, keysByTenant: {}, templatesByTenant: {}
    });
  },

  // Cierra la sesión live y REABRE el gate de conexión.
  disconnect() {
    clearCfg();
    setState({
      mode: 'demo', status: 'disconnected', demoChosen: false, error: null,
      tenants: DEMO_TENANTS, keysByTenant: {}, templatesByTenant: {}
    });
  },

  async loadTenants() {
    setState({ tenantsLoading: true });
    try {
      const j = await req('GET', '/admin/tenants?limit=500');
      const tenants = (j.data || []).map(adaptTenant);
      setState({ tenants, tenantsLoading: false });
      return tenants;
    } catch (e) { setState({ tenantsLoading: false }); throw e; }
  },

  async loadKeys(uuid) {
    const tName = (getSnapshot().tenants.find((t) => t.uuid === uuid) || {}).name;
    const set = (v) => setState({ keysByTenant: Object.assign({}, getSnapshot().keysByTenant, { [uuid]: v }) });
    set({ loading: true, error: null, data: (getSnapshot().keysByTenant[uuid] || {}).data || [] });
    try {
      const j = await req('GET', '/admin/tenants/' + uuid + '/apikeys');
      const data = (j.data || []).map((k) => adaptKey(k, tName));
      set({ loading: false, error: null, data });
      return data;
    } catch (e) { set({ loading: false, error: e.message, data: [] }); throw e; }
  },

  async issueKey(uuid, payload) {
    const j = await req('POST', '/admin/tenants/' + uuid + '/apikeys', payload);
    await this.loadKeys(uuid); // refresca la lista
    return j.data; // incluye raw_token (única vez)
  },

  async revokeKey(uuid, keyId) {
    await req('DELETE', '/admin/tenants/' + uuid + '/apikeys/' + keyId);
    await this.loadKeys(uuid);
  },

  async createTenant(payload) {
    const j = await req('POST', '/admin/tenants', payload);
    await this.loadTenants();
    return j.data;
  },

  async updateModules(uuid, modules) {
    await req('PUT', '/admin/tenants/' + uuid + '/modules', { modules });
    await this.loadTenants();
  },

  async loadTemplates(uuid) {
    const set = (v) => setState({ templatesByTenant: Object.assign({}, getSnapshot().templatesByTenant, { [uuid]: v }) });
    set({ loading: true, error: null, data: (getSnapshot().templatesByTenant[uuid] || {}).data || [] });
    try {
      const j = await req('GET', '/admin/tenants/' + uuid + '/templates');
      const data = (j.data || []).map(adaptTemplate);
      set({ loading: false, error: null, data });
      return data;
    } catch (e) { set({ loading: false, error: e.message, data: [] }); throw e; }
  },

  async syncTemplates(uuid) {
    await req('POST', '/admin/tenants/' + uuid + '/templates/sync');
    await this.loadTemplates(uuid);
  }
};

Object.assign(window, { BC, useBC, bcStore: { getSnapshot, subscribe, setState }, DEMO_TENANTS, DEMO_KEYS, DEMO_TEMPLATES });
