Nessun oggetto della modifica
Nessun oggetto della modifica
Riga 1: Riga 1:
/* ===================== MPAI – CHAT APP (no inline script) ===================== */
/* ===================== MPAI – CHAT APP (no inline script) ===================== */


// Guardia anti-doppio-caricamento (strumentata)
/** Guardia anti-doppio-caricamento (strumentata) */
if (window.__MP_DASHBOARD_LOADED__) {
if (window.__MP_DASHBOARD_LOADED__) {
   const src = (document.currentScript && document.currentScript.src) || '(inline)';
   const src = (document.currentScript && document.currentScript.src) || '(inline)';
Riga 17: Riga 15:
window.__MP_DASHBOARD_LOADED__ = true;
window.__MP_DASHBOARD_LOADED__ = true;


 
/** Se qualche script ha rotto $, lo ri-aggancio a jQuery */
// Se qualche script ha rotto $, lo ri-aggancio a jQuery
 
if (!(window.$ && window.$.fn && typeof window.$.Deferred === 'function')) {
if (!(window.$ && window.$.fn && typeof window.$.Deferred === 'function')) {
   window.$ = window.jQuery;
   window.$ = window.jQuery;
}
}


// Evita duplicato $mp: esponilo solo se non esiste
/** Shortcut locale */
 
window.$mp = window.$mp || (s => document.querySelector(s));
window.$mp = window.$mp || (s => document.querySelector(s));
// 4) WRAPPER: tutto il codice MPAI sta DENTRO questa IIFE.
//    Qui dentro puoi dichiarare tranquillamente qs/qsa: sono LOCALI (non globali).


(function () {
(function () {
   console.log('🧭 MPAI: init…');
   console.log('🧭 MPAI: init…');


   // Helper VANILLA **LOCALI** (non globali, niente conflitti)
   /* ===================== STATO ===================== */
 
   const mpaiState = {
   const qs  = (sel, root=document) => root.querySelector(sel);
    currentProject: null,   // nome progetto (se selezionato)
   const qsa = (sel, root=document) => Array.from(root.querySelectorAll(sel));
    sessionId: null,        // id bozza locale
    sessionMeta: null,      // {title, updated}
    history: [],
    attachments: []
   };


/* ============================================================
  /* ===================== UI HELPERS ===================== */
              MPAI – CHAT APP (no inline script)  
  function mpaiAppendMsg(role, content) {
=============================================================== */
    const chatEl = $mp('#mpai-chat');
    if (!chatEl) return;
    const div = document.createElement('div');
    div.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error'));
    div.innerHTML = `
      <div class="role">${role==='user'?'Tu':role==='assistant'?'GPT':'Errore'}</div>
      <div class="content" style="white-space:pre-wrap">${content}</div>`;
    chatEl.appendChild(div);
    chatEl.scrollTop = chatEl.scrollHeight;
  }


  function mpaiRenderFiles() {
    const fileListEl = $mp('#mpai-file-list');
    if (!fileListEl) return;
    fileListEl.innerHTML = '';
    mpaiState.attachments.forEach((a, i) => {
      const pill = document.createElement('span');
      pill.className = 'mpai-filepill';
      pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`;
      pill.querySelector('button').onclick = () => { mpaiState.attachments.splice(i, 1); mpaiRenderFiles(); };
      fileListEl.appendChild(pill);
    });
  }


  /* ===================== PERSISTENZA ===================== */
  function mpaiSaveLocal() {
    if (!mpaiState.currentProject) return;
    localStorage.setItem('mpai.hist.' + mpaiState.currentProject, JSON.stringify(mpaiState.history.slice(-200)));
  }
  function mpaiLoadLocal(project) {
    const chatEl = $mp('#mpai-chat');
    if (!chatEl) return;
    const raw = localStorage.getItem('mpai.hist.' + project);
    mpaiState.history = raw ? JSON.parse(raw) : [];
    chatEl.innerHTML = '';
    if (!mpaiState.history.length) {
      mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "' + project + '". Allegami anche file sanificati se servono.');
    } else {
      mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
    }
  }


 
  function mpaiEnsureSession() {
/** Mostra la UI AI e nasconde i box legacy */
     if (!mpaiState.sessionId) {
 
      mpaiState.sessionId = 'sess-' + Date.now();
window.showMpAI = function(){
      mpaiState.sessionMeta = { title: 'Nuova conversazione', updated: Date.now() };
  ['api-settings','project-status','chatgpt-plus','test-tools','activity-log']
      localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
     .forEach(id => { const el=document.getElementById(id); if(el) el.style.display='none'; });
     }
  const ai = document.getElementById('mpAI');
  if (ai){ ai.style.display='block'; ai.scrollIntoView({behavior:'smooth'}); }
};
 
/** Stato locale della chat */
 
const mpaiState = {
  currentProject: null,  // “progetto reale” (opzionale)
  sessionId: null,        // id bozza locale
  sessionMeta: null,      // {title, updated}
  history: [],
  attachments: []
};
 
/** Shortcuts */
 
const $mp = (s)=>document.querySelector(s);
 
/** UI helpers */
 
function mpaiAppendMsg(role, content){
  const chatEl = $mp('#mpai-chat');
  if (!chatEl) return;
  const div = document.createElement('div');
  div.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error'));
  div.innerHTML = `
    <div class="role">${role==='user'?'Tu':role==='assistant'?'GPT':'Errore'}</div>
    <div class="content" style="white-space:pre-wrap">${content}</div>`;
  chatEl.appendChild(div);
  chatEl.scrollTop = chatEl.scrollHeight;
}
 
function mpaiRenderFiles(){
  const fileListEl = $mp('#mpai-file-list');
  if (!fileListEl) return;
  fileListEl.innerHTML = '';
  mpaiState.attachments.forEach((a,i)=>{
    const pill = document.createElement('span');
    pill.className = 'mpai-filepill';
    pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`;
    pill.querySelector('button').onclick = ()=>{ mpaiState.attachments.splice(i,1); mpaiRenderFiles(); };
    fileListEl.appendChild(pill);
  });
}
 
/** Persistenza “per progetto” (cronologia locale) */
function mpaiSaveLocal(){
  if (!mpaiState.currentProject) return;
  localStorage.setItem('mpai.hist.'+mpaiState.currentProject, JSON.stringify(mpaiState.history.slice(-200)));
}
function mpaiLoadLocal(project){
  const chatEl = $mp('#mpai-chat');
  if (!chatEl) return;
  const raw = localStorage.getItem('mpai.hist.'+project);
  mpaiState.history = raw ? JSON.parse(raw) : [];
  chatEl.innerHTML = '';
  if (!mpaiState.history.length){
     mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". Allegami anche file sanificati se servono.');
  } else {
    mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
   }
   }
}
  function mpaiSaveSession() {
 
    if (!mpaiState.sessionId) return;
/** Bozze locali (se non c’è progetto) */
     const key = 'mpai.session.hist.' + mpaiState.sessionId;
function mpaiEnsureSession(){
    localStorage.setItem(key, JSON.stringify(mpaiState.history.slice(-200)));
  if (!mpaiState.sessionId){
     mpaiState.sessionMeta.updated = Date.now();
     mpaiState.sessionId = 'sess-' + Date.now();
     localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
     mpaiState.sessionMeta = { title: 'Nuova conversazione', updated: Date.now() };
     localStorage.setItem('mpai.session.meta.'+mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
   }
   }
}
   function mpaiLoadSession(id) {
function mpaiSaveSession(){
    const chatEl = $mp('#mpai-chat');
  if (!mpaiState.sessionId) return;
    if (!chatEl) return;
   const key = 'mpai.session.hist.' + mpaiState.sessionId;
    mpaiState.sessionId = id;
  localStorage.setItem(key, JSON.stringify(mpaiState.history.slice(-200)));
    const key = 'mpai.session.hist.' + id;
  mpaiState.sessionMeta.updated = Date.now();
    const raw = localStorage.getItem(key);
  localStorage.setItem('mpai.session.meta.'+mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
    mpaiState.history = raw ? JSON.parse(raw) : [];
}
    const metaRaw = localStorage.getItem('mpai.session.meta.' + id);
function mpaiLoadSession(id){
    mpaiState.sessionMeta = metaRaw ? JSON.parse(metaRaw) : { title: 'Nuova conversazione', updated: Date.now() };
  const chatEl = $mp('#mpai-chat');
    chatEl.innerHTML = '';
  if (!chatEl) return;
    if (!mpaiState.history.length) {
  mpaiState.sessionId = id;
      mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.');
  const key = 'mpai.session.hist.' + id;
    } else {
  const raw = localStorage.getItem(key);
      mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
  mpaiState.history = raw ? JSON.parse(raw) : [];
    }
  const metaRaw = localStorage.getItem('mpai.session.meta.'+id);
  mpaiState.sessionMeta = metaRaw ? JSON.parse(metaRaw) : {title:'Nuova conversazione', updated:Date.now()};
  chatEl.innerHTML = '';
  if (!mpaiState.history.length){
    mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.');
  } else {
    mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
   }
   }
}


/** Lista progetti (sidebar AI) – opzionale ma comoda */
  /* ===================== PROGETTI (sidebar) ===================== */
async function mpaiLoadProjects(){
  async function mpaiLoadProjects() {
  const box = $mp('#mpai-project-list');
    const box = $mp('#mpai-project-list');
  if (!box) return; // se non c’è la UI AI, non fare nulla
    if (!box) return;
  box.innerHTML = '<em class="muted">Carico progetti…</em>';
    box.innerHTML = '<em class="muted">Carico progetti…</em>';
  try{
    try {
    const r = await fetch('/dashboard/api/project_list.php', {cache:'no-store', credentials:'include'});
      const r = await fetch('/dashboard/api/project_list.php', { cache: 'no-store', credentials: 'include' });
    const j = await r.json();
      const j = await r.json();
    const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []);
      const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []);
    if (!arr.length){ box.innerHTML='<em class="muted">Nessun progetto</em>'; return; }
      if (!arr.length) { box.innerHTML = '<em class="muted">Nessun progetto</em>'; return; }
    box.innerHTML='';
      box.innerHTML = '';
    arr.forEach(item=>{
      arr.forEach(item => {
      const name = (typeof item === 'string') ? item : (item.name || item);
        const name = (typeof item === 'string') ? item : (item.name || item);
      const row = document.createElement('div');
        const row = document.createElement('div');
      row.className = 'row';
        row.className = 'row';
      row.innerHTML = `<span class="title">${name}</span>
        row.innerHTML = `<span class="title">${name}</span>
                      <span><button class="mpai-btn" data-open="${name}">Apri</button></span>`;
                        <span><button class="mpai-btn" data-open="${name}">Apri</button></span>`;
      row.querySelector('[data-open]').onclick = ()=>{  
        row.querySelector('[data-open]').onclick = () => {
        mpaiState.currentProject = name;  
          mpaiState.currentProject = name;
        mpaiLoadLocal(name);  
          mpaiLoadLocal(name);
      };
        };
      box.appendChild(row);
        box.appendChild(row);
    });
      });
    if (!mpaiState.currentProject){
      if (!mpaiState.currentProject) {
      const first = (typeof arr[0]==='string')?arr[0]:(arr[0].name||arr[0]);
        const first = (typeof arr[0] === 'string') ? arr[0] : (arr[0].name || arr[0]);
      mpaiState.currentProject = first;
        mpaiState.currentProject = first;
      mpaiLoadLocal(first);
        mpaiLoadLocal(first);
      }
    } catch (e) {
      box.innerHTML = '<span class="muted">Errore caricamento: ' + e.message + '</span>';
     }
     }
  }catch(e){
    box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>';
   }
   }
}
async function mpaiCreateProject(){
  const inp = $mp('#mpai-project-name');
  const name = (inp?.value||'').trim();
  if (!name){ alert('Inserisci un nome progetto'); return; }
  try{
    const r = await fetch('/dashboard/api/project_create.php', {
      method:'POST',
      headers:{'Content-Type':'application/json'},
      credentials:'include',
      body: JSON.stringify({name})
    });
    const txt = await r.text(); let j; try{ j=JSON.parse(txt) }catch{ j={ok:false,raw:txt}; }
    if (j.ok!==false){ await mpaiLoadProjects(); mpaiState.currentProject=name; mpaiState.history=[]; mpaiLoadLocal(name); inp.value=''; }
    else { alert('Errore creazione: '+(j.error||'')); }
  }catch(e){ alert('Errore rete: '+e.message); }
}


  async function mpaiCreateProject() {
    const inp = $mp('#mpai-project-name');
    const name = (inp?.value || '').trim();
    if (!name) { alert('Inserisci un nome progetto'); return; }
    try {
      const r = await fetch('/dashboard/api/project_create.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ name })
      });
      const txt = await r.text(); let j; try { j = JSON.parse(txt) } catch { j = { ok: false, raw: txt }; }
      if (j.ok !== false) { await mpaiLoadProjects(); mpaiState.currentProject = name; mpaiState.history = []; mpaiLoadLocal(name); inp.value = ''; }
      else { alert('Errore creazione: ' + (j.error || '')); }
    } catch (e) { alert('Errore rete: ' + e.message); }
  }
  /* ===================== INVIO PROMPT → PROXY ===================== */
  window.sendPrompt = async function () {
    try {
      const ta = document.querySelector('#mpai-input');
      if (!ta) return;


/** INVIO PROMPT → OpenAI proxy (versione robusta) */
      const content = (ta.value || '').trim();
window.sendPrompt = async function () {
      if (!content) { ta.focus(); return; }
  try {
    const ta = document.querySelector('#mpai-input');
    if (!ta) return;


    const content = (ta.value || '').trim();
      // mostra subito il messaggio utente
    if (!content) { ta.focus(); return; }
      mpaiState.history.push({ role: 'user', content });
      mpaiAppendMsg('user', content);
      ta.value = '';


    // mostra subito il messaggio utente
      const model = (document.querySelector('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();
    mpaiAppendMsg('user', content);
    ta.value = '';


    const model = (document.querySelector('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();
      // placeholder "Elaboro…"
      const pending = document.createElement('div');
      pending.className = 'mpai-msg';
      pending.innerHTML = '<em class="muted">Elaboro…</em>';
      document.querySelector('#mpai-chat')?.appendChild(pending);


    // placeholder "Elaboro…"
      const r = await fetch('/dashboard/api/openai_project_gpt.php', {
    const pending = document.createElement('div');
        method: 'POST',
    pending.className = 'mpai-msg';
        headers: { 'Content-Type': 'application/json' },
    pending.innerHTML = '<em class="muted">Elaboro…</em>';
        credentials: 'include',
    document.querySelector('#mpai-chat')?.appendChild(pending);
        cache: 'no-store',
        body: JSON.stringify({ model, prompt: content })
      });


    const r = await fetch('/dashboard/api/openai_project_gpt.php', {
      const text = await r.text();
       method: 'POST',
       console.log('HTTP', r.status, r.statusText);
      headers: {'Content-Type': 'application/json'},
       console.log('RAW:', text);
       credentials: 'include',
       pending.remove?.();
      cache: 'no-store',
       body: JSON.stringify({ model, prompt: content })
    });


    const text = await r.text();
      if (!r.ok) {
    pending.remove?.();
        mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${text || '(nessun body)'}`);
        return;
      }


    if (!r.ok) {
      let j;
       mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${text || '(nessun body)'}`);
      try {
      return;
        j = JSON.parse(text);
    }
        if (typeof j === 'string') { try { j = JSON.parse(j); } catch {} }
        console.log('PARSED:', j);
       } catch (e) {
        console.error('PARSE_ERR', e);
        mpaiAppendMsg('error', `Risposta non in JSON:\n\n${text.slice(0, 1500)}`);
        return;
      }


    let j;
      if (j && j.status === 'ok' && j.result) {
    try {
        mpaiState.history.push({ role: 'assistant', content: j.result });
      j = JSON.parse(text);
        mpaiAppendMsg('assistant', j.result);
       if (typeof j === 'string') { try { j = JSON.parse(j); } catch {} } // tollera doppia codifica
        mpaiSaveLocal();
       } else {
        mpaiAppendMsg('error', `API error: ${(j && (j.error || j.message)) || 'sconosciuto'}\n\nRAW:\n${text.slice(0, 1500)}`);
      }
     } catch (e) {
     } catch (e) {
       mpaiAppendMsg('error', `Risposta non in JSON:\n\n${text.slice(0,1500)}`);
       mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`);
      return;
     }
     }
  };


    if (j && j.status === 'ok' && j.result) {
  /* ===================== SHOW UI + BIND UNA SOLA VOLTA ===================== */
      mpaiAppendMsg('assistant', j.result);
  window.showMpAI = function () {
    } else {
    ['api-settings', 'project-status', 'chatgpt-plus', 'test-tools', 'activity-log']
       mpaiAppendMsg('error', `API error: ${(j && (j.error || j.message)) || 'sconosciuto'}\n\nRAW:\n${text.slice(0,1500)}`);
       .forEach(id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; });
     }
    const ai = document.getElementById('mpAI');
  } catch (e) {
     if (ai) { ai.style.display = 'block'; ai.scrollIntoView({ behavior: 'smooth' }); }
    mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`);
    mpaiBindUI();  // assicura i listener una sola volta
   }
   };
};


  function mpaiBindUI() {
    if (window.__MP_AI_BOUND__) return;  // evita doppi binding
    window.__MP_AI_BOUND__ = true;


/** Bind UI quando la pagina è pronta (solo se la UI AI esiste) */
    if (!$mp('#mpAI')) return; // UI non presente
document.addEventListener('DOMContentLoaded', ()=>{
  if (!$mp('#mpAI')) return; // non siamo nella dashboard AI


  // Upload locale (solo elenco)
    // Upload locale (solo elenco)
  const dz = $mp('#mpai-dropzone');
    const dz = $mp('#mpai-dropzone');
  const fi = $mp('#mpai-file-input');
    const fi = $mp('#mpai-file-input');
  dz && dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
    dz && dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
  dz && dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
    dz && dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
  dz && dz.addEventListener('drop', e => {
    dz && dz.addEventListener('drop', e => {
    e.preventDefault(); dz.style.opacity = 1;
      e.preventDefault(); dz.style.opacity = 1;
    Array.from(e.dataTransfer.files||[]).forEach(f=> mpaiState.attachments.push({file:f,name:f.name}));
      Array.from(e.dataTransfer.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name }));
    mpaiRenderFiles();
      mpaiRenderFiles();
  });
    });
  fi && fi.addEventListener('change', () => {
    fi && fi.addEventListener('change', () => {
    Array.from(fi.files||[]).forEach(f=> mpaiState.attachments.push({file:f,name:f.name}));
      Array.from(fi.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name }));
    fi.value=''; mpaiRenderFiles();
      fi.value = ''; mpaiRenderFiles();
  });
    });


  // Bottoni
    // Bottoni
  $mp('#mpai-send')?.addEventListener('click', window.sendPrompt);
    $mp('#mpai-send')?.addEventListener('click', window.sendPrompt);
  $mp('#mpai-project-create')?.addEventListener('click', mpaiCreateProject);
    $mp('#mpai-project-create')?.addEventListener('click', mpaiCreateProject);
  $mp('#mpai-new-chat')?.addEventListener('click', ()=>{
    $mp('#mpai-new-chat')?.addEventListener('click', () => {
    mpaiState.history=[]; mpaiState.attachments=[]; mpaiRenderFiles();
      mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles();
    const chatEl = $mp('#mpai-chat'); if (chatEl){ chatEl.innerHTML=''; }
      const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; }
    if (mpaiState.currentProject){ mpaiAppendMsg('assistant','Nuova chat per "'+mpaiState.currentProject+'".'); mpaiSaveLocal(); }
      if (mpaiState.currentProject) { mpaiAppendMsg('assistant', 'Nuova chat per "' + mpaiState.currentProject + '".'); mpaiSaveLocal(); }
    else { mpaiState.sessionId=null; mpaiState.sessionMeta=null; mpaiEnsureSession(); mpaiAppendMsg('assistant','Nuova conversazione.'); mpaiSaveSession(); }
      else { mpaiState.sessionId = null; mpaiState.sessionMeta = null; mpaiEnsureSession(); mpaiAppendMsg('assistant', 'Nuova conversazione.'); mpaiSaveSession(); }
  });
    });
  $mp('#mpai-clear')?.addEventListener('click', ()=>{
    $mp('#mpai-clear')?.addEventListener('click', () => {
    if (!mpaiState.currentProject){ alert('Crea o seleziona un progetto'); return; }
      if (!mpaiState.currentProject) { alert('Crea o seleziona un progetto'); return; }
    if (!confirm('Svuotare la conversazione locale?')) return;
      if (!confirm('Svuotare la conversazione locale?')) return;
    mpaiState.history=[]; mpaiState.attachments=[]; mpaiRenderFiles();
      mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles();
    const chatEl = $mp('#mpai-chat'); if (chatEl){ chatEl.innerHTML=''; }
      const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; }
    mpaiAppendMsg('assistant','Conversazione pulita.'); mpaiSaveLocal();
      mpaiAppendMsg('assistant', 'Conversazione pulita.'); mpaiSaveLocal();
  });
    });
 
    // Invio con Cmd/Ctrl+Enter nella textarea
    document.addEventListener('keydown', (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
        const ta = $mp('#mpai-input');
        if (ta && ta === document.activeElement) { e.preventDefault(); window.sendPrompt(); }
      }
    });


  // Invio con Cmd/Ctrl+Enter nella textarea
    // Prima boot
  document.addEventListener('keydown', (e)=>{
    mpaiLoadProjects();
    if ((e.metaKey||e.ctrlKey) && e.key==='Enter'){ const ta=$mp('#mpai-input'); if (ta && ta===document.activeElement){ e.preventDefault(); window.sendPrompt(); } }
    console.log('🔗 MPAI listeners collegati.');
   });
  }
 
  // Bind on ready (se la UI è già presente)
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => { if ($mp('#mpAI')) mpaiBindUI(); });
  } else {
    if ($mp('#mpAI')) mpaiBindUI();
   }


  // Boot
  mpaiLoadProjects();
});
   console.log('✅ MPAI: pronta.');
   console.log('✅ MPAI: pronta.');
})();
})();

Versione delle 08:04, 30 set 2025

/* ===================== MPAI – CHAT APP (no inline script) ===================== */

/** Guardia anti-doppio-caricamento (strumentata) */
if (window.__MP_DASHBOARD_LOADED__) {
  const src = (document.currentScript && document.currentScript.src) || '(inline)';
  try {
    const u = new URL(src, location.origin);
    console.warn('MP Dashboard già caricata – stop. Source:', src, 'from=', u.searchParams.get('from') || '(n/a)');
  } catch {
    console.warn('MP Dashboard già caricata – stop. Source:', src);
  }
  console.trace('Stack doppio load');
  throw new Error('MP Dashboard già caricata');
}
window.__MP_DASHBOARD_LOADED__ = true;

/** Se qualche script ha rotto $, lo ri-aggancio a jQuery */
if (!(window.$ && window.$.fn && typeof window.$.Deferred === 'function')) {
  window.$ = window.jQuery;
}

/** Shortcut locale */
window.$mp = window.$mp || (s => document.querySelector(s));

(function () {
  console.log('🧭 MPAI: init…');

  /* ===================== STATO ===================== */
  const mpaiState = {
    currentProject: null,   // nome progetto (se selezionato)
    sessionId: null,        // id bozza locale
    sessionMeta: null,      // {title, updated}
    history: [],
    attachments: []
  };

  /* ===================== UI HELPERS ===================== */
  function mpaiAppendMsg(role, content) {
    const chatEl = $mp('#mpai-chat');
    if (!chatEl) return;
    const div = document.createElement('div');
    div.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error'));
    div.innerHTML = `
      <div class="role">${role==='user'?'Tu':role==='assistant'?'GPT':'Errore'}</div>
      <div class="content" style="white-space:pre-wrap">${content}</div>`;
    chatEl.appendChild(div);
    chatEl.scrollTop = chatEl.scrollHeight;
  }

  function mpaiRenderFiles() {
    const fileListEl = $mp('#mpai-file-list');
    if (!fileListEl) return;
    fileListEl.innerHTML = '';
    mpaiState.attachments.forEach((a, i) => {
      const pill = document.createElement('span');
      pill.className = 'mpai-filepill';
      pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`;
      pill.querySelector('button').onclick = () => { mpaiState.attachments.splice(i, 1); mpaiRenderFiles(); };
      fileListEl.appendChild(pill);
    });
  }

  /* ===================== PERSISTENZA ===================== */
  function mpaiSaveLocal() {
    if (!mpaiState.currentProject) return;
    localStorage.setItem('mpai.hist.' + mpaiState.currentProject, JSON.stringify(mpaiState.history.slice(-200)));
  }
  function mpaiLoadLocal(project) {
    const chatEl = $mp('#mpai-chat');
    if (!chatEl) return;
    const raw = localStorage.getItem('mpai.hist.' + project);
    mpaiState.history = raw ? JSON.parse(raw) : [];
    chatEl.innerHTML = '';
    if (!mpaiState.history.length) {
      mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "' + project + '". Allegami anche file sanificati se servono.');
    } else {
      mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
    }
  }

  function mpaiEnsureSession() {
    if (!mpaiState.sessionId) {
      mpaiState.sessionId = 'sess-' + Date.now();
      mpaiState.sessionMeta = { title: 'Nuova conversazione', updated: Date.now() };
      localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
    }
  }
  function mpaiSaveSession() {
    if (!mpaiState.sessionId) return;
    const key = 'mpai.session.hist.' + mpaiState.sessionId;
    localStorage.setItem(key, JSON.stringify(mpaiState.history.slice(-200)));
    mpaiState.sessionMeta.updated = Date.now();
    localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
  }
  function mpaiLoadSession(id) {
    const chatEl = $mp('#mpai-chat');
    if (!chatEl) return;
    mpaiState.sessionId = id;
    const key = 'mpai.session.hist.' + id;
    const raw = localStorage.getItem(key);
    mpaiState.history = raw ? JSON.parse(raw) : [];
    const metaRaw = localStorage.getItem('mpai.session.meta.' + id);
    mpaiState.sessionMeta = metaRaw ? JSON.parse(metaRaw) : { title: 'Nuova conversazione', updated: Date.now() };
    chatEl.innerHTML = '';
    if (!mpaiState.history.length) {
      mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.');
    } else {
      mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
    }
  }

  /* ===================== PROGETTI (sidebar) ===================== */
  async function mpaiLoadProjects() {
    const box = $mp('#mpai-project-list');
    if (!box) return;
    box.innerHTML = '<em class="muted">Carico progetti…</em>';
    try {
      const r = await fetch('/dashboard/api/project_list.php', { cache: 'no-store', credentials: 'include' });
      const j = await r.json();
      const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []);
      if (!arr.length) { box.innerHTML = '<em class="muted">Nessun progetto</em>'; return; }
      box.innerHTML = '';
      arr.forEach(item => {
        const name = (typeof item === 'string') ? item : (item.name || item);
        const row = document.createElement('div');
        row.className = 'row';
        row.innerHTML = `<span class="title">${name}</span>
                         <span><button class="mpai-btn" data-open="${name}">Apri</button></span>`;
        row.querySelector('[data-open]').onclick = () => {
          mpaiState.currentProject = name;
          mpaiLoadLocal(name);
        };
        box.appendChild(row);
      });
      if (!mpaiState.currentProject) {
        const first = (typeof arr[0] === 'string') ? arr[0] : (arr[0].name || arr[0]);
        mpaiState.currentProject = first;
        mpaiLoadLocal(first);
      }
    } catch (e) {
      box.innerHTML = '<span class="muted">Errore caricamento: ' + e.message + '</span>';
    }
  }

  async function mpaiCreateProject() {
    const inp = $mp('#mpai-project-name');
    const name = (inp?.value || '').trim();
    if (!name) { alert('Inserisci un nome progetto'); return; }
    try {
      const r = await fetch('/dashboard/api/project_create.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ name })
      });
      const txt = await r.text(); let j; try { j = JSON.parse(txt) } catch { j = { ok: false, raw: txt }; }
      if (j.ok !== false) { await mpaiLoadProjects(); mpaiState.currentProject = name; mpaiState.history = []; mpaiLoadLocal(name); inp.value = ''; }
      else { alert('Errore creazione: ' + (j.error || '')); }
    } catch (e) { alert('Errore rete: ' + e.message); }
  }

  /* ===================== INVIO PROMPT → PROXY ===================== */
  window.sendPrompt = async function () {
    try {
      const ta = document.querySelector('#mpai-input');
      if (!ta) return;

      const content = (ta.value || '').trim();
      if (!content) { ta.focus(); return; }

      // mostra subito il messaggio utente
      mpaiState.history.push({ role: 'user', content });
      mpaiAppendMsg('user', content);
      ta.value = '';

      const model = (document.querySelector('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();

      // placeholder "Elaboro…"
      const pending = document.createElement('div');
      pending.className = 'mpai-msg';
      pending.innerHTML = '<em class="muted">Elaboro…</em>';
      document.querySelector('#mpai-chat')?.appendChild(pending);

      const r = await fetch('/dashboard/api/openai_project_gpt.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        cache: 'no-store',
        body: JSON.stringify({ model, prompt: content })
      });

      const text = await r.text();
      console.log('HTTP', r.status, r.statusText);
      console.log('RAW:', text);
      pending.remove?.();

      if (!r.ok) {
        mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${text || '(nessun body)'}`);
        return;
      }

      let j;
      try {
        j = JSON.parse(text);
        if (typeof j === 'string') { try { j = JSON.parse(j); } catch {} }
        console.log('PARSED:', j);
      } catch (e) {
        console.error('PARSE_ERR', e);
        mpaiAppendMsg('error', `Risposta non in JSON:\n\n${text.slice(0, 1500)}`);
        return;
      }

      if (j && j.status === 'ok' && j.result) {
        mpaiState.history.push({ role: 'assistant', content: j.result });
        mpaiAppendMsg('assistant', j.result);
        mpaiSaveLocal();
      } else {
        mpaiAppendMsg('error', `API error: ${(j && (j.error || j.message)) || 'sconosciuto'}\n\nRAW:\n${text.slice(0, 1500)}`);
      }
    } catch (e) {
      mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`);
    }
  };

  /* ===================== SHOW UI + BIND UNA SOLA VOLTA ===================== */
  window.showMpAI = function () {
    ['api-settings', 'project-status', 'chatgpt-plus', 'test-tools', 'activity-log']
      .forEach(id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; });
    const ai = document.getElementById('mpAI');
    if (ai) { ai.style.display = 'block'; ai.scrollIntoView({ behavior: 'smooth' }); }
    mpaiBindUI();  // assicura i listener una sola volta
  };

  function mpaiBindUI() {
    if (window.__MP_AI_BOUND__) return;   // evita doppi binding
    window.__MP_AI_BOUND__ = true;

    if (!$mp('#mpAI')) return; // UI non presente

    // Upload locale (solo elenco)
    const dz = $mp('#mpai-dropzone');
    const fi = $mp('#mpai-file-input');
    dz && dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
    dz && dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
    dz && dz.addEventListener('drop', e => {
      e.preventDefault(); dz.style.opacity = 1;
      Array.from(e.dataTransfer.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name }));
      mpaiRenderFiles();
    });
    fi && fi.addEventListener('change', () => {
      Array.from(fi.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name }));
      fi.value = ''; mpaiRenderFiles();
    });

    // Bottoni
    $mp('#mpai-send')?.addEventListener('click', window.sendPrompt);
    $mp('#mpai-project-create')?.addEventListener('click', mpaiCreateProject);
    $mp('#mpai-new-chat')?.addEventListener('click', () => {
      mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles();
      const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; }
      if (mpaiState.currentProject) { mpaiAppendMsg('assistant', 'Nuova chat per "' + mpaiState.currentProject + '".'); mpaiSaveLocal(); }
      else { mpaiState.sessionId = null; mpaiState.sessionMeta = null; mpaiEnsureSession(); mpaiAppendMsg('assistant', 'Nuova conversazione.'); mpaiSaveSession(); }
    });
    $mp('#mpai-clear')?.addEventListener('click', () => {
      if (!mpaiState.currentProject) { alert('Crea o seleziona un progetto'); return; }
      if (!confirm('Svuotare la conversazione locale?')) return;
      mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles();
      const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; }
      mpaiAppendMsg('assistant', 'Conversazione pulita.'); mpaiSaveLocal();
    });

    // Invio con Cmd/Ctrl+Enter nella textarea
    document.addEventListener('keydown', (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
        const ta = $mp('#mpai-input');
        if (ta && ta === document.activeElement) { e.preventDefault(); window.sendPrompt(); }
      }
    });

    // Prima boot
    mpaiLoadProjects();
    console.log('🔗 MPAI listeners collegati.');
  }

  // Bind on ready (se la UI è già presente)
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => { if ($mp('#mpAI')) mpaiBindUI(); });
  } else {
    if ($mp('#mpAI')) mpaiBindUI();
  }

  console.log('✅ MPAI: pronta.');
})();