// Google Slides integration — OAuth token client + Slides API helpers.
//
// Usage:
//   await window.gSlides.connect()                       // OAuth popup, stores token
//   await window.gSlides.createFromTemplate(tmpl)        // → { presentationId, url }
//   window.gSlides.isConnected()                          // bool
//   window.gSlides.profile()                              // { email, name } | null
//   window.gSlides.disconnect()
//
// Tokens are kept in localStorage so the connection survives a refresh
// within the token's lifetime (~1 hour). Refresh tokens aren't available
// in the implicit / token-client flow — the user re-clicks "Connect" when
// it expires (or we silently re-prompt with prompt='' to refresh).
//
// Scopes:
//   presentations  — create/edit Google Slides
//   drive.file     — the created files show up in the user's Drive (only
//                    the files our app created — not full Drive access)

(function () {
  const STORAGE_KEY = 'emphasis.google.token.v1';
  const SCOPES = [
    'https://www.googleapis.com/auth/presentations',
    'https://www.googleapis.com/auth/drive.file',
    'https://www.googleapis.com/auth/userinfo.email',
    'https://www.googleapis.com/auth/userinfo.profile',
  ].join(' ');

  function readStored() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return null;
      const t = JSON.parse(raw);
      if (!t.accessToken || !t.expiresAt) return null;
      if (Date.now() > t.expiresAt - 30_000) return null; // expired or close to it
      return t;
    } catch { return null; }
  }
  function writeStored(t) { localStorage.setItem(STORAGE_KEY, JSON.stringify(t)); }
  function clearStored()  { localStorage.removeItem(STORAGE_KEY); }

  let currentToken = readStored();
  let currentProfile = currentToken?.profile || null;

  async function fetchProfile(accessToken) {
    try {
      const r = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
        headers: { Authorization: `Bearer ${accessToken}` },
      });
      if (!r.ok) return null;
      return await r.json(); // { email, name, picture, ... }
    } catch { return null; }
  }

  function isConfigured() {
    const id = window.EMPHASIS_ENV?.googleClientId || '';
    return id && !id.startsWith('REPLACE_');
  }

  function isConnected() {
    return !!readStored();
  }

  async function connect(opts = {}) {
    if (!isConfigured()) {
      throw new Error('Google Client ID ยังไม่ได้ตั้งใน lib/config.jsx (googleClientId)');
    }
    if (!window.google?.accounts?.oauth2) {
      throw new Error('Google Identity script ยังโหลดไม่เสร็จ ลองใหม่อีกครั้ง');
    }
    return new Promise((resolve, reject) => {
      const client = window.google.accounts.oauth2.initTokenClient({
        client_id: window.EMPHASIS_ENV.googleClientId,
        scope: SCOPES,
        // 'consent' on first link, '' for silent reuse afterwards.
        prompt: opts.silent ? '' : 'consent',
        callback: async (resp) => {
          if (resp.error) {
            reject(new Error(resp.error_description || resp.error));
            return;
          }
          const expiresAt = Date.now() + (resp.expires_in || 3600) * 1000;
          const profile = await fetchProfile(resp.access_token);
          currentToken = { accessToken: resp.access_token, expiresAt, profile };
          currentProfile = profile;
          writeStored(currentToken);
          resolve({ accessToken: resp.access_token, profile });
        },
        error_callback: (err) => reject(new Error(err.message || 'Google OAuth ถูกยกเลิก')),
      });
      client.requestAccessToken();
    });
  }

  function disconnect() {
    currentToken = null;
    currentProfile = null;
    clearStored();
    // Best-effort revoke so the user can re-consent from scratch.
    try {
      const id = currentToken?.accessToken;
      if (id && window.google?.accounts?.oauth2?.revoke) {
        window.google.accounts.oauth2.revoke(id, () => {});
      }
    } catch {}
  }

  function token() {
    const t = readStored();
    return t?.accessToken || null;
  }

  function profile() {
    const t = readStored();
    return t?.profile || null;
  }

  async function ensureToken() {
    let t = readStored();
    if (t) return t.accessToken;
    // Try a silent refresh first; if it pops a popup, that's also fine.
    const fresh = await connect({ silent: true }).catch(() => null);
    if (fresh) return fresh.accessToken;
    throw new Error('ยังไม่ได้เชื่อมต่อ Google — ไปที่ Settings → Integrations → Connect Google');
  }

  // Build a presentation from a template spec.
  //
  // tmpl shape:
  //   {
  //     title: 'My deck',
  //     slides: [
  //       { title: 'Slide 1', body: ['Bullet A', 'Bullet B'] },
  //       ...
  //     ],
  //   }
  //
  // Returns: { presentationId, url }.
  async function createFromTemplate(tmpl) {
    const accessToken = await ensureToken();

    // 1) Create the deck with the title.
    const createRes = await fetch('https://slides.googleapis.com/v1/presentations', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ title: tmpl.title || 'EMPHASIS Template' }),
    });
    if (!createRes.ok) {
      const body = await createRes.text();
      throw new Error('สร้าง deck ไม่สำเร็จ: ' + body);
    }
    const pres = await createRes.json();
    const presentationId = pres.presentationId;
    const firstSlideId = pres.slides?.[0]?.objectId;

    // 2) Build batchUpdate requests for each template slide.
    //    Strategy:
    //      - Replace the first auto-created blank slide's title/body via
    //        the slide's placeholder text elements (we look up the
    //        existing placeholder objectIds from the create response).
    //      - For each additional slide, createSlide with TITLE_AND_BODY
    //        layout, then fill its placeholders by index.
    const requests = [];

    // Helper to set placeholder text on a slide by walking the response's
    // slide.pageElements once we have them. Since the create response
    // returns the first auto slide's elements but not subsequent ones,
    // we instead use a 2-step approach:
    //   (a) Create all slides up-front
    //   (b) Re-fetch the presentation to know each slide's placeholder ids
    //   (c) Issue insertText for each one
    // This is simpler than the alternative of guessing placeholder ids.

    // (a) Create N-1 extra slides (first is auto-created).
    const slides = tmpl.slides || [];
    for (let i = 1; i < slides.length; i++) {
      requests.push({
        createSlide: {
          insertionIndex: String(i),
          slideLayoutReference: { predefinedLayout: 'TITLE_AND_BODY' },
        },
      });
    }
    if (requests.length) {
      const r2 = await fetch(`https://slides.googleapis.com/v1/presentations/${presentationId}:batchUpdate`, {
        method: 'POST',
        headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ requests }),
      });
      if (!r2.ok) console.warn('createSlide batch failed:', await r2.text());
    }

    // (b) Re-fetch the deck to enumerate placeholder objectIds per slide.
    const getRes = await fetch(`https://slides.googleapis.com/v1/presentations/${presentationId}`, {
      headers: { Authorization: `Bearer ${accessToken}` },
    });
    const deck = await getRes.json();

    // (c) Build text-fill requests.
    const textReqs = [];
    (deck.slides || []).forEach((slide, idx) => {
      const spec = slides[idx];
      if (!spec) return;
      // Find title + body placeholders.
      let titleId = null;
      let bodyId = null;
      for (const el of slide.pageElements || []) {
        const ph = el.shape?.placeholder;
        if (!ph) continue;
        if (ph.type === 'TITLE' || ph.type === 'CENTERED_TITLE') titleId = el.objectId;
        else if (ph.type === 'BODY' || ph.type === 'SUBTITLE') bodyId = el.objectId;
      }
      if (titleId && spec.title) {
        textReqs.push({
          insertText: { objectId: titleId, text: spec.title, insertionIndex: 0 },
        });
      }
      if (bodyId && spec.body) {
        const text = Array.isArray(spec.body)
          ? spec.body.map(b => '• ' + b).join('\n')
          : String(spec.body);
        textReqs.push({
          insertText: { objectId: bodyId, text, insertionIndex: 0 },
        });
      }
    });

    if (textReqs.length) {
      const r3 = await fetch(`https://slides.googleapis.com/v1/presentations/${presentationId}:batchUpdate`, {
        method: 'POST',
        headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
        body: JSON.stringify({ requests: textReqs }),
      });
      if (!r3.ok) console.warn('insertText batch failed:', await r3.text());
    }

    return {
      presentationId,
      url: `https://docs.google.com/presentation/d/${presentationId}/edit`,
    };
  }

  window.gSlides = {
    isConfigured,
    isConnected,
    connect,
    disconnect,
    token,
    profile,
    createFromTemplate,
  };
})();
