// contacts/app.js — XDock Plus (MV3)
// - CRUD contacts (POST via bg.js), GET direct API robuste
// - Gestion groupes (groups.*), CORS ok
// - Drag & Drop: réordonner + déplacer entre groupes
// - IDs normalisés

// ====== Config API ======
const API_BASE = "https://www.gp-eff.fr/api/xdock/contacts"; // Migré vers MyTruck NAS
const API_KEY  = "xdockplus"; // doit matcher XDOCK_CONTACTS_API_KEY backend
const API = {
  groups: {
    list:   { url: API_BASE, action: 'groups.list'   },
    create: { url: API_BASE, action: 'groups.create' },
    rename: { url: API_BASE, action: 'groups.rename' },
    del:    { url: API_BASE, action: 'groups.delete' },
    reorder:{ url: API_BASE, action: 'groups.reorder' },
    reorderContacts:{ url: API_BASE, action: 'contacts.reorder' },
    move:   { url: API_BASE, action: 'contacts.move' },
  },
};

// ====== Utils ======
const DBG = false;
const log = (...a) => DBG && console.log("[contacts]", ...a);
function normPhone(p){ const d = String(p||"").replace(/[^\d+]/g,""); return d ? (d.startsWith("+")?d:"+"+d) : ""; }
function callNumber(phone){
  const num = normPhone(phone); if (!num) return;
  try {
    if (window.parent && window.parent!==window) {
      window.parent.postMessage({xdockplus:true,type:"CALL",phone:num},"*");
    } else { location.href = "ciscotel:" + num; }
  } catch(e){ console.warn("callNumber error", e); }
}
function alertBox(msg){ try{ alert("L’extension XDock Plus indique\n\n"+msg); }catch{ console.warn(msg); } }
function sendBg(payload){ return new Promise((resolve)=>{ try{ chrome.runtime.sendMessage(payload,(res)=>resolve(res||{ok:false})); }catch(e){ resolve({ok:false,error:String(e)}); } }); }
function escapeHtml(s){ return String(s||"").replace(/[&<>"']/g, m=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[m])); }
function uid(){ return Math.random().toString(36).slice(2,10); }

// ====== SVG ======
function phoneSvg(){return `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M6.62 10.79a15.05 15.05 0 0 0 6.59 6.59l2.2-2.2a1 1 0 0 1 1.11-.21c1.21.49 2.53.76 3.88.76a1 1 0 0 1 1 1v3.5a1 1 0 0 1-1 1C10.3 22.13 1.88 13.7 1.88 4a1 1 0 0 1 1-1h3.5a1 1 0 0 1 1 1c0 1.35.26 2.67.76 3.88a1 1 0 0 1-.21 1.11z"/></svg>`;}
function editSvg(){return `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04a1.003 1.003 0 0 0 0-1.42l-2.34-2.34a1.003 1.003 0 0 0-1.42 0l-1.83 1.83 3.75 3.75 1.84-1.82z"/></svg>`;}
function trashSvg(){return `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M16 4V3a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v1H3v2h18V4zM8 4V3h8v1zM6 7h12v14H6z"/></svg>`;}

// ====== Sélecteurs ======
const els = {
  grid:    document.getElementById('groups-grid'),
  sel:     document.getElementById('group'),
  name:    document.getElementById('name'),
  phone:   document.getElementById('phone'),
  save:    document.getElementById('save'),
  reset:   document.getElementById('reset'),
  search:  document.getElementById('search'),
  // modal groupes
  btnManage:  document.getElementById('btn-manage-groups'),
  modal:      document.getElementById('groups-modal'),
  modalClose: document.getElementById('groups-close'),
  gList:      document.getElementById('groups-list'),
  gNewName:   document.getElementById('new-group-name'),
  gCreate:    document.getElementById('create-group'),
};

// ====== État ======
const state = {
  groups: [],       // [{id, name, sort_order}]
  contacts: [],     // [{id, name, phone, group_id, sort_order}]
  search: '',
  editingId: null,
};

// ====== Fallback groupes (si API absente) ======
const LS_GROUPS = 'contacts_groups_v1';
function loadGroupsFallback(){
  let g = JSON.parse(localStorage.getItem(LS_GROUPS) || 'null');
  if (!g) {
    g = [
      { id: 1, name: 'Matin' },
      { id: 2, name: 'Après-midi' },
      { id: 3, name: 'Nuit' },
      { id: 4, name: 'Prestation' },
      { id: 5, name: 'Groupage' },
    ];
    localStorage.setItem(LS_GROUPS, JSON.stringify(g));
  }
  return g;
}
function saveGroupsFallback(groups){ localStorage.setItem(LS_GROUPS, JSON.stringify(groups)); }

// ====== Fetch JSON helper (CORS friendly) ======
async function fetchJSON(url, body){
  const res = await fetch(url, {
    method: 'POST',
    mode: 'cors',
    headers: { 'Content-Type':'application/json', 'Accept':'application/json', 'X-API-Key': API_KEY },
    body: JSON.stringify(body || {})
  });
  const text = await res.text().catch(()=> "");
  let json = null; try { json = JSON.parse(text); } catch {}
  if (!res.ok) throw new Error(`HTTP ${res.status}${text ? `\n${text}` : ''}`);
  if (!json) throw new Error('Réponse non-JSON');
  return json;
}

// ====== Chargement groupes ======
async function loadGroups(){
  try {
    const data = await fetchJSON(API.groups.list.url, { action: API.groups.list.action });
    const rows = (data.data || data) || [];
    if (Array.isArray(rows)) {
      state.groups = rows.map(g => ({
        id:Number(g.id), name:String(g.name).trim(), sort_order:Number(g.sort_order||0)
      })).sort((a,b)=> (a.sort_order||0)-(b.sort_order||0) || a.name.localeCompare(b.name));
    } else {
      state.groups = Object.keys(rows).map((name, i) => ({ id:i+1, name: String(name).trim(), sort_order:i+1 }));
    }
  } catch {
    state.groups = loadGroupsFallback().map((g,idx)=>({ ...g, name:String(g.name).trim(), sort_order:idx+1 }));
  }
  renderGroupsUI();
}

// ====== Rendu UI ======
function renderGroupsUI(){
  // Select
  if (els.sel) {
    els.sel.innerHTML = state.groups.map(g => `<option value="${g.id}">${escapeHtml(g.name)}</option>`).join('');
    if (!state.groups.some(g=>String(g.id)===els.sel.value)) els.sel.value = state.groups[0]?.id || '';
  }

  // Grille
  if (!els.grid) return;
  const byGroup = new Map(state.groups.map(g => [g.id, []]));
  const q = (state.search||'').toLowerCase();

  for (const c of state.contacts) {
    if (!byGroup.has(c.group_id)) continue;
    const ok = !q || (c.name && c.name.toLowerCase().includes(q)) || String(c.phone).includes(q);
    if (ok) byGroup.get(c.group_id).push(c);
  }

  els.grid.innerHTML = state.groups.map(g => {
    const list = byGroup.get(g.id) || [];
    list.sort((a,b)=> (a.sort_order||0)-(b.sort_order||0) || String(a.name).localeCompare(String(b.name)));
    const items = list.map(contactItemHTML).join('') || `<div style="color:#6b7280">Aucun contact</div>`;
    return `
      <div class="card">
        <h2>${escapeHtml(g.name)}</h2>
        <div class="list" data-group="${g.id}">
          ${items}
        </div>
      </div>
    `;
  }).join('');
}

function contactItemHTML(c){
  return `
    <div class="contact" draggable="true" data-id="${String(c.id)}">
      <div class="info">
        <div class="name">${escapeHtml(c.name)}</div>
        <div class="phone">${escapeHtml(c.phone)}</div>
      </div>
      <div class="actions">
        <button type="button" class="icon-btn call" title="Appeler" aria-label="Appeler" style="background:#007AFF;color:#fff;border:none">
          ${phoneSvg()}
        </button>
        <button type="button" class="icon-btn edit" title="Modifier" aria-label="Modifier" style="background:#FBBF24;color:#111;border:none">
          ${editSvg()}
        </button>
        <button type="button" class="icon-btn del" title="Supprimer" aria-label="Supprimer" style="background:#EF4444;color:#fff;border:none">
          ${trashSvg()}
        </button>
      </div>
    </div>
  `;
}

function render(){ renderGroupsUI(); }

// ====== Contacts (GET direct API tolérant) ======
async function loadContacts(){
  try {
    const r = await fetch(API_BASE, { cache:"no-store", headers: { 'X-API-Key': API_KEY, 'Accept': 'application/json' } });
    const text = await r.text();
    let d;
    try { d = JSON.parse(text); }
    catch(e){ alertBox("Réponse GET non-JSON\nHTTP "+r.status+"\n"+text); return; }
    if (!r.ok) { alertBox("GET contacts HTTP "+r.status); return; }

    if (d && d.ok && d.data !== undefined) d = d.data;

    const contacts = [];
    const nameToId = new Map(state.groups.map(g => [String(g.name).trim().toLowerCase(), Number(g.id)]));

    if (Array.isArray(d)) {
      for (const c of d) {
        const gid = Number(c.group_id) || state.groups[0]?.id || 1;
        contacts.push({ id:Number(c.id), name:c.name||'', phone:c.phone||'', group_id:gid, sort_order:Number(c.sort_order||0) });
      }
    } else if (d && typeof d === 'object') {
      for (const [rawName, arr] of Object.entries(d)) {
        const gid = nameToId.get(String(rawName).trim().toLowerCase()) ?? state.groups[0]?.id ?? 1;
        let list = [];
        if (Array.isArray(arr)) list = arr;
        else if (arr && typeof arr === 'object') list = Array.isArray(arr.items) ? arr.items : (Array.isArray(arr.data) ? arr.data : []);
        for (const c of list) {
          contacts.push({ id:Number(c.id), name:c.name||'', phone:c.phone||'', group_id:Number(c.group_id||gid), sort_order:Number(c.sort_order||0) });
        }
      }
    } else {
      console.warn("[contacts] Format GET inattendu:", d);
      alertBox("Format de données inattendu (voir console).");
      return;
    }

    state.contacts = contacts;
    render();
  } catch (err) {
    console.error("loadContacts error", err);
    alertBox("Impossible de charger les contacts.\n"+String(err));
  }
}

// ====== CRUD via background ======
async function upsertContact(contact){
  const res = await sendBg({ type:"CONTACTS_POST", body:{ action:"upsert", contact } });
  if (!res?.ok) { alertBox("Erreur sauvegarde (transport)"); throw new Error("save transport"); }
  if (!res.data) { alertBox("Erreur sauvegarde: réponse invalide"); throw new Error("save nojson"); }
  if (res.data.ok === true) { await loadContacts(); return; }
  const err = res.data.error || "inconnue";
  if (err === "duplicate_phone") alertBox("Numéro déjà présent (doublon).");
  else if (err === "invalid_phone") alertBox("Numéro invalide.");
  else if (err === "invalid_fields") alertBox("Champs invalides (nom, téléphone ou groupe).");
  else alertBox("Erreur sauvegarde: " + err);
  throw new Error("save api: " + err);
}
async function deleteContact(id){
  const res = await sendBg({ type:"CONTACTS_POST", body:{ action:"delete", id: Number(id) } });
  if (!res?.ok || !res.data?.ok) { alertBox("Erreur suppression"); return; }
  await loadContacts();
}

// ====== Form ======
function startEdit(c){
  state.editingId = Number(c.id) || null;
  els.name.value = c.name || "";
  els.phone.value = c.phone || "";
  els.sel.value = state.groups.some(g => g.id === c.group_id) ? c.group_id : state.groups[0]?.id;
  els.save.textContent = "Mettre à jour";
}
function resetForm(){
  state.editingId = null;
  els.name.value = "";
  els.phone.value = "";
  els.sel.selectedIndex = 0;
  els.save.textContent = "Ajouter";
}

// ====== UI bindings ======
function bindUI(){
  els.save.onclick = async ()=>{
    const name = els.name.value.trim();
    const phone = els.phone.value.trim();
    const group_id = Number(els.sel.value || state.groups[0]?.id || 1);
    if (!name || !phone || !group_id) { alertBox("Nom, téléphone et groupe requis"); return; }
    const contact = { id: state.editingId, name, phone, group_id };
    try { await upsertContact(contact); resetForm(); } catch(e){ console.error(e); }
  };
  els.reset.onclick = resetForm;
  els.search.oninput = (e)=>{ state.search = e.target.value || ""; render(); };

  // Click actions
  els.grid.addEventListener('click', (e)=>{
    const root = e.target.closest('.contact'); if (!root) return;
    const id = Number(root.dataset.id);
    const c = state.contacts.find(x => Number(x.id) === id);
    if (!c) return;
    if (e.target.closest('.call'))  { callNumber(c.phone); return; }
    if (e.target.closest('.edit'))  { startEdit(c); return; }
    if (e.target.closest('.del'))   { deleteContact(Number(c.id)); return; }
  });

  // ----- Drag & Drop contacts (reorder + move to another group) -----
  let dragContactEl = null;
  let dragContactId = null;

  els.grid.addEventListener('dragstart', (e)=>{
    const item = e.target.closest('.contact');
    if (!item) return;
    dragContactEl = item;
    dragContactId = Number(item.dataset.id);
    e.dataTransfer.effectAllowed = 'move';
  });

  els.grid.addEventListener('dragover', (e)=>{
    const over = e.target.closest('.contact, .list');
    if (!over) return;
    e.preventDefault();
  });

  els.grid.addEventListener('drop', async (e)=>{
    e.preventDefault();
    const dropOnContact = e.target.closest('.contact');
    const list = e.target.closest('.list');        // target list
    if (!dragContactEl || !list) return;

    const toGroupId = Number(list.dataset.group);
    const contact   = state.contacts.find(c => Number(c.id) === dragContactId);
    if (!contact) { dragContactEl = null; dragContactId = null; return; }

    // --- CASE A: move to another group
    if (contact.group_id !== toGroupId) {
      try {
        if (dropOnContact && dropOnContact.parentElement === list) {
          dropOnContact.before(dragContactEl);
        } else {
          list.appendChild(dragContactEl);
        }

        await sendBg({ type: "CONTACTS_POST", body: { action: "contacts.move", id: contact.id, to_group_id: toGroupId } });

        contact.group_id = toGroupId;
        contact.sort_order = Math.max( ...state.contacts.filter(c=>c.group_id===toGroupId).map(c=>c.sort_order||0), 0 ) + 1;

        render();
      } catch (err) {
        console.error("move failed", err);
        alertBox("Déplacement du contact impossible.");
      } finally {
        dragContactEl = null; dragContactId = null;
      }
      return;
    }

    // --- CASE B: same group → reordering
    const nodes = Array.from(list.children).filter(x=>x.classList.contains('contact'));

    if (dropOnContact && dropOnContact.parentElement === list) {
      const from = nodes.indexOf(dragContactEl);
      const to   = nodes.indexOf(dropOnContact);
      if (from !== -1 && to !== -1 && from !== to) {
        if (from < to) dropOnContact.after(dragContactEl); else dropOnContact.before(dragContactEl);
      }
    } else {
      list.appendChild(dragContactEl);
    }

    const newIds = Array.from(list.querySelectorAll('.contact')).map(el => Number(el.dataset.id));
    try {
      await sendBg({ type: "CONTACTS_POST", body: { action: "contacts.reorder", group_id: contact.group_id, ids: newIds } });
      const groupContacts = state.contacts.filter(c=>c.group_id===contact.group_id);
      groupContacts.sort((a,b)=> newIds.indexOf(Number(a.id)) - newIds.indexOf(Number(b.id)));
      const others = state.contacts.filter(c=>c.group_id!==contact.group_id);
      state.contacts = [...others, ...groupContacts];
    } catch (err) {
      console.error("reorder failed", err);
      alertBox("Réordonnancement impossible.");
    } finally {
      dragContactEl = null; dragContactId = null;
    }
  });
}

// ====== Modal Groupes (CRUD + DnD) ======
function openModal(){ els.modal.style.display = 'block'; }
function closeModal(){ els.modal.style.display = 'none'; }

async function refreshGroupsModal(){
  els.gList.innerHTML = state.groups.map((g) => `
    <li data-id="${g.id}" draggable="true"
        style="display:flex;align-items:center;gap:10px;justify-content:space-between;border:1px solid #e5e7eb;border-radius:10px;padding:10px;background:#fff">
      <span class="drag" title="Glisser pour réordonner" style="cursor:grab">≡</span>
      <input class="g-name" value="${escapeHtml(g.name)}" style="flex:1;border:1px solid #e5e7eb;border-radius:8px;height:36px;padding:0 10px"/>
      <div style="display:flex;gap:8px">
        <button class="secondary btn-rename">Renommer</button>
        <button class="icon-btn del btn-delete" title="Supprimer" style="background:#EF4444;color:#fff;border:none">${trashSvg()}</button>
      </div>
    </li>
  `).join('');
}

function enableGroupsDnD(){
  let dragEl = null;
  els.gList.querySelectorAll('li[draggable="true"]').forEach(li=>{
    li.addEventListener('dragstart', (e)=>{ dragEl = li; e.dataTransfer.effectAllowed = 'move'; });
    li.addEventListener('dragover', (e)=>{ e.preventDefault(); });
    li.addEventListener('drop', async (e)=>{
      e.preventDefault();
      const target = e.currentTarget;
      if (!dragEl || dragEl === target) return;

      const nodes = Array.from(els.gList.children);
      const dragIndex = nodes.indexOf(dragEl);
      const targetIndex = nodes.indexOf(target);
      if (dragIndex < targetIndex) target.after(dragEl); else target.before(dragEl);

      const ids = Array.from(els.gList.children).map(li => Number(li.dataset.id));
      try {
        await fetchJSON(API.groups.reorder.url, { action: API.groups.reorder.action, ids });
        state.groups.sort((a,b)=> ids.indexOf(a.id) - ids.indexOf(b.id));
        renderGroupsUI();
      } catch (e) {
        alertBox("Réordonnancement groupes: " + e.message);
      }
    });
  });
}

async function onCreateGroup(){
  const name = (els.gNewName.value||'').trim(); if (!name) return;
  try {
    const res = await fetchJSON(API.groups.create.url, { action: API.groups.create.action, name });
    state.groups.push({ id:Number(res.id||Date.now()), name, sort_order: (state.groups[state.groups.length-1]?.sort_order||0)+1 });
  } catch (e) {
    alertBox("Création groupe: " + e.message);
    return;
  }
  els.gNewName.value = '';
  renderGroupsUI();
  await refreshGroupsModal(); enableGroupsDnD();
}

async function onGroupsListClick(e){
  const li = e.target.closest('li[data-id]'); if (!li) return;
  const id = Number(li.dataset.id);
  if (e.target.closest('.btn-delete')) {
    if (!confirm('Supprimer ce groupe ?')) return;
    try { await fetchJSON(API.groups.del.url, { action: API.groups.del.action, id }); }
    catch (e) { alertBox("Suppression groupe: " + e.message); return; }
    state.contacts = state.contacts.filter(c => c.group_id !== id);
    state.groups = state.groups.filter(g => g.id !== id);
    renderGroupsUI(); await refreshGroupsModal(); enableGroupsDnD(); return;
  }
  if (e.target.closest('.btn-rename')) {
    const name = li.querySelector('.g-name').value.trim(); if (!name) return;
    try { await fetchJSON(API.groups.rename.url, { action: API.groups.rename.action, id, name }); }
    catch (e) { alertBox("Renommage groupe: " + e.message); return; }
    const g = state.groups.find(x=>x.id===id); if (g) g.name = name;
    renderGroupsUI(); await refreshGroupsModal(); enableGroupsDnD(); return;
  }
}

// ====== Init ======
(function init(){
  bindUI();
  (async () => {
    await loadGroups();     // charge groupes
    await loadContacts();   // charge contacts
    // Graines si vide (facultatif dev)
    if (state.contacts.length === 0 && state.groups.length) {
      const g0 = state.groups[0].id;
      state.contacts.push({ id: Number(Date.now()%1e9), name:'Alice Dupont', phone:'+33600000001', group_id:g0, sort_order: 9999 });
      render();
    }
  })();

  // Ouvrir/fermer modal
  els.btnManage?.addEventListener('click', async ()=>{ await loadGroups(); await refreshGroupsModal(); enableGroupsDnD(); openModal(); });
  els.modalClose?.addEventListener('click', closeModal);
  els.modal?.addEventListener('click', (e)=>{ if (e.target===els.modal) closeModal(); });
  els.gCreate?.addEventListener('click', onCreateGroup);
  els.gList?.addEventListener('click', onGroupsListClick);
})();
