// gmail.jsx — GỬI EMAIL THẬT qua Gmail API bằng tài khoản Google @swo.vn đang đăng nhập.
// Dùng Google Identity Services (token client, client-side, không backend).
// Bật bằng GOOGLE_OAUTH_CLIENT_ID trong config.jsx + cấu hình Google Cloud (xem docs/gmail-send.md).
// Rỗng → window.GMail.configured() = false → app giữ chế độ mock (không gửi thật).
(function(){
  const CLIENT_ID = (window.SWO_CONFIG && window.SWO_CONFIG.GOOGLE_OAUTH_CLIENT_ID) || '';
  const SCOPE = 'https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/gmail.readonly';
  let tokenClient = null, token = null, tokenExp = 0;

  function gisReady(){ return !!(window.google && google.accounts && google.accounts.oauth2); }
  function configured(){ return !!CLIENT_ID; }

  function ensureClient(){
    if (tokenClient || !CLIENT_ID || !gisReady()) return tokenClient;
    tokenClient = google.accounts.oauth2.initTokenClient({ client_id: CLIENT_ID, scope: SCOPE, callback: () => {} });
    return tokenClient;
  }

  // Cache token trong phiên (sessionStorage) để mở email nhiều lần KHÔNG xin lại;
  // cờ "đã đồng ý" (localStorage) để các lần sau xin token im lặng (prompt:'') thay vì ép màn consent.
  const TOK_KEY = 'swo-portal-gmailTok', GRANT_KEY = 'swo-portal-gmailGranted';
  (function restore(){
    try { const s = JSON.parse(sessionStorage.getItem(TOK_KEY) || 'null'); if (s && s.t && s.e) { token = s.t; tokenExp = s.e; } } catch (_){}
  })();
  function granted(){ try { return localStorage.getItem(GRANT_KEY) === '1'; } catch (_){ return false; } }

  // Lấy access token (lần đầu hiện màn đồng ý; sau đó im lặng — không hỏi lại mỗi lần mở).
  function getToken(hint){
    return new Promise((resolve, reject) => {
      if (!CLIENT_ID) { reject(new Error('Chưa cấu hình GOOGLE_OAUTH_CLIENT_ID')); return; }
      if (!gisReady()) { reject(new Error('Google Identity Services chưa tải xong — thử lại sau giây lát')); return; }
      ensureClient();
      if (token && Date.now() < tokenExp - 60000) { resolve(token); return; }
      tokenClient.callback = (resp) => {
        if (resp && resp.error) { reject(new Error(resp.error_description || resp.error)); return; }
        token = resp.access_token; tokenExp = Date.now() + ((resp.expires_in || 3600) * 1000);
        try { sessionStorage.setItem(TOK_KEY, JSON.stringify({ t:token, e:tokenExp })); } catch (_){}
        try { localStorage.setItem(GRANT_KEY, '1'); } catch (_){}
        resolve(token);
      };
      // Đã từng đồng ý → xin im lặng (prompt:''); chưa từng → hiện màn consent 1 lần.
      try { tokenClient.requestAccessToken({ prompt: granted() ? '' : 'consent', hint: hint || '' }); }
      catch (e) { reject(e); }
    });
  }

  function b64(str){ return btoa(unescape(encodeURIComponent(str))); }
  function b64url(str){ return b64(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); }

  // Dựng RFC 2822 (UTF-8: subject encoded-word, body charset UTF-8) → base64url cho Gmail API.
  // m.attachments = [{ name, type, b64 }] (b64 = base64 chuẩn của nội dung file).
  function buildRaw(m){
    const enc = (s) => '=?UTF-8?B?' + b64(s || '') + '?=';
    const atts = m.attachments || [];
    const h = [];
    if (m.from) h.push('From: ' + m.from);
    h.push('To: ' + (m.to || []).join(', '));
    if (m.cc && m.cc.length) h.push('Cc: ' + m.cc.join(', '));
    h.push('Subject: ' + enc(m.subject || '(no subject)'));
    h.push('MIME-Version: 1.0');
    if (!atts.length){
      h.push('Content-Type: text/html; charset="UTF-8"');
      return b64url(h.join('\r\n') + '\r\n\r\n' + (m.html || ''));
    }
    const bnd = 'swo_' + Math.abs(Date.now()).toString(36) + '_' + (m.to ? m.to.length : 0);
    h.push('Content-Type: multipart/mixed; boundary="' + bnd + '"');
    let body = h.join('\r\n') + '\r\n\r\n';
    body += '--' + bnd + '\r\nContent-Type: text/html; charset="UTF-8"\r\n\r\n' + (m.html || '') + '\r\n';
    atts.forEach(a => {
      const wrapped = (a.b64 || '').replace(/.{76}/g, '$&\r\n');
      body += '--' + bnd + '\r\n';
      body += 'Content-Type: ' + (a.type || 'application/octet-stream') + '; name="' + a.name + '"\r\n';
      body += 'Content-Transfer-Encoding: base64\r\n';
      body += 'Content-Disposition: attachment; filename="' + a.name + '"\r\n\r\n';
      body += wrapped + '\r\n';
    });
    body += '--' + bnd + '--';
    return b64url(body);
  }

  // send({ to:[], cc:[], subject, html, from, hint }) → Promise(resolve khi Gmail nhận; reject kèm message lỗi)
  async function send(msg){
    const accessToken = await getToken(msg.hint || msg.from);
    const res = await fetch('https://gmail.googleapis.com/gmail/v1/users/me/messages/send', {
      method: 'POST',
      headers: { 'Authorization': 'Bearer ' + accessToken, 'Content-Type': 'application/json' },
      body: JSON.stringify({ raw: buildRaw(msg) }),
    });
    if (!res.ok){
      let m = 'Gmail API lỗi ' + res.status;
      try { const e = await res.json(); if (e && e.error && e.error.message) m = e.error.message; } catch (_){}
      throw new Error(m);
    }
    return res.json();
  }

  // ====== ĐỌC HỘP THƯ (Gmail API read) ======
  // base64url → chuỗi UTF-8 (giải mã body/part của Gmail).
  function b64urlDecode(s){
    if (!s) return '';
    let t = s.replace(/-/g, '+').replace(/_/g, '/');
    while (t.length % 4) t += '=';
    try { return decodeURIComponent(escape(atob(t))); } catch (_){ try { return atob(t); } catch (e){ return ''; } }
  }
  function header(headers, name){
    const h = (headers || []).find(x => x.name && x.name.toLowerCase() === name.toLowerCase());
    return h ? h.value : '';
  }
  // Duyệt cây MIME: lấy html (ưu tiên) hoặc text, và liệt kê đính kèm.
  function walk(part, acc){
    if (!part) return;
    const mime = part.mimeType || '';
    const fn = part.filename || '';
    if (fn && part.body && part.body.attachmentId){
      acc.attachments.push({ name:fn, type:mime, size:part.body.size || 0, attachmentId:part.body.attachmentId });
    } else if (mime === 'text/html' && part.body && part.body.data){
      acc.html = b64urlDecode(part.body.data);
    } else if (mime === 'text/plain' && part.body && part.body.data && !acc.text){
      acc.text = b64urlDecode(part.body.data);
    }
    (part.parts || []).forEach(p => walk(p, acc));
  }

  async function api(path, accessToken){
    const res = await fetch('https://gmail.googleapis.com/gmail/v1/users/me' + path, {
      headers: { 'Authorization': 'Bearer ' + accessToken },
    });
    if (!res.ok){
      let m = 'Gmail API lỗi ' + res.status;
      try { const e = await res.json(); if (e && e.error && e.error.message) m = e.error.message; } catch (_){}
      throw new Error(m);
    }
    return res.json();
  }

  // Chạy fn() theo từng đợt giới hạn số request song song (Gmail chặn "too many concurrent requests").
  async function mapPool(items, limit, fn){
    const out = new Array(items.length);
    let i = 0;
    async function worker(){ while (i < items.length){ const idx = i++; out[idx] = await fn(items[idx], idx); } }
    await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
    return out;
  }

  // listInbox({ q, max, hint }) → [{ id, threadId, from, fromName, subject, date, snippet, unread, hasAttach }]
  async function listInbox(opts){
    opts = opts || {};
    const accessToken = await getToken(opts.hint);
    const max = opts.max || 25;
    const q = (opts.q || '').trim();
    let path = '/messages?maxResults=' + max + '&labelIds=INBOX';
    if (q) path += '&q=' + encodeURIComponent(q);
    const list = await api(path, accessToken);
    const ids = (list.messages || []).map(m => m.id);
    // Lấy metadata từng mail (header + snippet) — nhẹ hơn format=full; giới hạn 4 request song song.
    const metaPath = (id) => '/messages/' + id + '?format=metadata&metadataHeaders=From&metadataHeaders=Subject&metadataHeaders=Date';
    const msgs = await mapPool(ids, 4, (id) => api(metaPath(id), accessToken));
    return msgs.map(msg => {
      const hs = (msg.payload && msg.payload.headers) || [];
      const from = header(hs, 'From');
      const nameMatch = from.match(/^\s*"?([^"<]*?)"?\s*<.*>/);
      const labels = msg.labelIds || [];
      return {
        id: msg.id, threadId: msg.threadId,
        from, fromName: (nameMatch && nameMatch[1].trim()) || from,
        subject: header(hs, 'Subject') || '(không tiêu đề)',
        date: header(hs, 'Date'),
        snippet: msg.snippet || '',
        unread: labels.indexOf('UNREAD') !== -1,
        hasAttach: false,
      };
    });
  }

  // getMessage(id) → { id, from, fromName, to, subject, date, html, text, attachments[] }
  async function getMessage(id, hint){
    const accessToken = await getToken(hint);
    const msg = await api('/messages/' + id + '?format=full', accessToken);
    const hs = (msg.payload && msg.payload.headers) || [];
    const acc = { html:'', text:'', attachments:[] };
    walk(msg.payload, acc);
    const from = header(hs, 'From');
    const nameMatch = from.match(/^\s*"?([^"<]*?)"?\s*<.*>/);
    return {
      id: msg.id, threadId: msg.threadId,
      from, fromName: (nameMatch && nameMatch[1].trim()) || from,
      to: header(hs, 'To'), cc: header(hs, 'Cc'),
      subject: header(hs, 'Subject') || '(không tiêu đề)',
      date: header(hs, 'Date'),
      html: acc.html, text: acc.text,
      attachments: acc.attachments,
    };
  }

  // getAttachment(messageId, attachmentId) → chuỗi base64url nội dung file (tải khi user bấm).
  async function getAttachment(messageId, attachmentId, hint){
    const accessToken = await getToken(hint);
    const data = await api('/messages/' + messageId + '/attachments/' + attachmentId, accessToken);
    return data.data || '';
  }

  window.GMail = { configured, gisReady, send, buildRaw, listInbox, getMessage, getAttachment };
})();
