// Облачная синхронизация аннотаций и закладок по «sync-коду» (без аккаунтов).
// Бэкенд: POST /cloud/kv/get {code, since} и /cloud/kv/set {code, items:[{key,value}]}.
// Слияние — объединение массивов по id (без потери данных между устройствами).

import { getSettings } from "../common/storage.js";

async function cfg() {
  const s = await getSettings();
  return {
    enabled: !!s.syncEnabled,
    code: (s.syncCode || "").trim(),
    url: (s.syncUrl || s.backendUrl || "").replace(/\/+$/, ""),
    token: s.backendToken || "",
  };
}

export async function syncEnabled() {
  const c = await cfg();
  return c.enabled && c.code.length >= 8 && !!c.url;
}

function headers(token) {
  return { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}) };
}
async function kvGet(c, since) {
  const r = await fetch(`${c.url}/cloud/kv/get`, { method: "POST", headers: headers(c.token), body: JSON.stringify({ code: c.code, since }) });
  if (!r.ok) throw new Error(`kv/get ${r.status}`);
  return r.json();
}
async function kvSet(c, items) {
  const r = await fetch(`${c.url}/cloud/kv/set`, { method: "POST", headers: headers(c.token), body: JSON.stringify({ code: c.code, items }) });
  if (!r.ok) throw new Error(`kv/set ${r.status}`);
  return r.json();
}

const safeParse = (s) => { try { return JSON.parse(s); } catch { return null; } };

/** Ключ дедупликации элемента массива в зависимости от типа данных. */
function itemKey(storageKey, x) {
  if (storageKey.startsWith("annotations_")) return x.id;
  if (storageKey.startsWith("bookmarks_")) return `${x.page}|${x.title}|${x.ai ? 1 : 0}`;
  return JSON.stringify(x);
}
/** Объединение локального и удалённого массивов без потерь (локальный приоритет при совпадении ключа). */
function mergeArr(storageKey, local, remote) {
  const map = new Map();
  for (const x of remote || []) map.set(itemKey(storageKey, x), x);
  for (const x of local || []) map.set(itemKey(storageKey, x), x);
  return [...map.values()];
}

/** Отправить текущее значение ключа на сервер (после локального изменения). */
export async function pushKey(key) {
  if (!(await syncEnabled())) return;
  try {
    const c = await cfg();
    const { [key]: val = [] } = await chrome.storage.local.get(key);
    const res = await kvSet(c, [{ key, value: JSON.stringify(val) }]);
    await chrome.storage.local.set({ _syncRev: res.rev });
  } catch (e) {
    console.warn("[Aris sync] push:", e.message);
  }
}

/**
 * Синхронизировать заданные ключи: забрать дельту, объединить локально,
 * дослать объединённое (сервер сходится). Возвращает {changed:[ключи]}.
 */
export async function syncKeys(keys) {
  if (!(await syncEnabled())) return { changed: [] };
  const c = await cfg();
  const changed = [];
  try {
    const { _syncRev = 0 } = await chrome.storage.local.get("_syncRev");
    const { entries, rev } = await kvGet(c, _syncRev);
    const remote = {};
    for (const e of entries) remote[e.key] = safeParse(e.value);
    const toPush = [];
    for (const key of keys) {
      if (!(key in remote) || remote[key] == null) continue;
      const { [key]: local = [] } = await chrome.storage.local.get(key);
      const merged = mergeArr(key, local, remote[key]);
      if (JSON.stringify(merged) !== JSON.stringify(local)) {
        await chrome.storage.local.set({ [key]: merged });
        changed.push(key);
      }
      if (JSON.stringify(merged) !== JSON.stringify(remote[key])) {
        toPush.push({ key, value: JSON.stringify(merged) });
      }
    }
    let newRev = rev;
    if (toPush.length) newRev = Math.max(rev, (await kvSet(c, toPush)).rev);
    await chrome.storage.local.set({ _syncRev: newRev });
  } catch (e) {
    console.warn("[Aris sync] syncKeys:", e.message);
  }
  return { changed };
}

/** Сгенерировать случайный sync-код. */
export function generateCode() {
  const a = new Uint8Array(12);
  crypto.getRandomValues(a);
  return [...a].map((b) => b.toString(36).padStart(2, "0")).join("").slice(0, 20);
}
