/**
 * PrivacyPopup.js
 * Privacy authorization popup for WeChat mini-game.
 *
 * When a privacy-sensitive API (e.g. wx.getUserInfo) is called, WeChat
 * triggers `wx.onNeedPrivacyAuthorization`. This popup shows a compliant
 * dialog that explains what data we collect and lets the user agree or
 * decline before we call `resolve({ buttonAction: 'agree' })`.
 *
 * Compliance notes (WeChat 2024+):
 *   - Must clearly state what data is collected and why.
 *   - Must provide a way to decline (user can still play with placeholder).
 *   - Must link to the full privacy policy.
 */

const {
  SCREEN_WIDTH,
  SCREEN_HEIGHT,
} = require('../base/GameGlobal');

const { t } = require('../i18n/I18n');

// Colors
const OVERLAY_BG     = 'rgba(0, 0, 0, 0.75)';
const DIALOG_BG      = '#1e1e32';
const DIALOG_BORDER  = '#4a90d9';
const TITLE_COLOR    = '#FFD700';
const TEXT_COLOR      = '#CCCCCC';
const LINK_COLOR     = '#4a90d9';
const AGREE_BG       = '#4a90d9';
const AGREE_TEXT     = '#FFFFFF';
const DECLINE_BG     = 'rgba(255, 255, 255, 0.08)';
const DECLINE_BORDER = '#666666';
const DECLINE_TEXT   = '#AAAAAA';

class PrivacyPopup {
  constructor() {
    /** @type {boolean} Whether the popup is currently visible. */
    this._active = false;

    /** @type {Function|null} The resolve callback from onNeedPrivacyAuthorization. */
    this._resolveCallback = null;

    /** @type {string} Which API triggered the privacy request. */
    this._referrer = '';

    /** @type {object|null} Button hit-test rects (computed during render). */
    this._agreeBtn = null;
    this._declineBtn = null;
    this._policyLink = null;
  }

  // ============================================================
  // Public API
  // ============================================================

  get active() {
    return this._active;
  }

  /**
   * Show the privacy popup.
   * @param {Function} resolve - The resolve callback from onNeedPrivacyAuthorization.
   * @param {object} eventInfo - The eventInfo from onNeedPrivacyAuthorization.
   */
  show(resolve, eventInfo) {
    // Guard: if popup is already active, chain the new resolve callback
    // so that when the current popup resolves, both callbacks get called.
    // This prevents showing two popups at once.
    if (this._active && this._resolveCallback) {
      console.warn('[PrivacyPopup] Already active, chaining resolve callback. referrer:', this._referrer, ', new referrer:', (eventInfo && eventInfo.referrer) || '');
      const prevResolve = this._resolveCallback;
      this._resolveCallback = (result) => {
        try { prevResolve(result); } catch (e) { /* ignore */ }
        try { resolve(result); } catch (e) { /* ignore */ }
      };
      return;
    }

    this._active = true;
    this._resolveCallback = resolve;
    this._referrer = (eventInfo && eventInfo.referrer) || '';
    console.log('[PrivacyPopup] Showing popup, referrer:', this._referrer);
  }

  /**
   * Hide the popup (without resolving — used internally after resolve is called).
   */
  hide() {
    this._active = false;
    this._resolveCallback = null;
    this._agreeBtn = null;
    this._declineBtn = null;
    this._policyLink = null;
  }

  /**
   * Handle touch event. Returns true if the touch was consumed.
   * @param {string} eventType - 'touchstart' | 'touchend'
   * @param {object} e - The touch event object.
   * @returns {boolean}
   */
  handleTouch(eventType, e) {
    if (!this._active) return false;
    if (eventType !== 'touchend') return true; // Consume touchstart to prevent bleed-through

    const touch = e.changedTouches && e.changedTouches[0];
    if (!touch) return true;

    const tx = touch.clientX;
    const ty = touch.clientY;

    // Agree button
    if (this._agreeBtn && this._hitTest(tx, ty, this._agreeBtn)) {
      console.log('[PrivacyPopup] User tapped AGREE');
      this._resolve('agree');
      return true;
    }

    // Decline button
    if (this._declineBtn && this._hitTest(tx, ty, this._declineBtn)) {
      console.log('[PrivacyPopup] User tapped DECLINE');
      this._resolve('disagree');
      return true;
    }

    // Privacy policy link
    if (this._policyLink && this._hitTest(tx, ty, this._policyLink)) {
      console.log('[PrivacyPopup] User tapped privacy policy link');
      this._openPrivacyPolicy();
      return true;
    }

    return true; // Consume all touches while popup is active
  }

  // ============================================================
  // Render
  // ============================================================

  render(ctx) {
    if (!this._active) return;

    const cw = SCREEN_WIDTH;
    const ch = SCREEN_HEIGHT;
    const cx = cw / 2;
    const cy = ch / 2;

    // --- Semi-transparent overlay ---
    ctx.fillStyle = OVERLAY_BG;
    ctx.fillRect(0, 0, cw, ch);

    // --- Dialog box ---
    const dialogW = Math.min(cw * 0.88, 420);
    const dialogH = Math.min(ch * 0.78, 320);
    const dialogX = cx - dialogW / 2;
    const dialogY = cy - dialogH / 2;

    // Shadow
    ctx.fillStyle = 'rgba(0, 0, 0, 0.4)';
    this._roundRect(ctx, dialogX + 4, dialogY + 4, dialogW, dialogH, 12);
    ctx.fill();

    // Background
    ctx.fillStyle = DIALOG_BG;
    this._roundRect(ctx, dialogX, dialogY, dialogW, dialogH, 12);
    ctx.fill();

    // Border
    ctx.strokeStyle = DIALOG_BORDER;
    ctx.lineWidth = 1.5;
    this._roundRect(ctx, dialogX, dialogY, dialogW, dialogH, 12);
    ctx.stroke();

    // --- Shield icon (Canvas path) ---
    const iconY = dialogY + 32;
    const sw = 22; // shield width
    const sh = 26; // shield height
    const sx = cx - sw / 2;
    const sy = iconY - sh / 2;

    // Shield body
    ctx.fillStyle = DIALOG_BORDER;
    ctx.beginPath();
    ctx.moveTo(sx, sy);
    ctx.lineTo(sx + sw, sy);
    ctx.lineTo(sx + sw, sy + sh * 0.55);
    ctx.quadraticCurveTo(sx + sw, sy + sh * 0.85, cx, sy + sh);
    ctx.quadraticCurveTo(sx, sy + sh * 0.85, sx, sy + sh * 0.55);
    ctx.closePath();
    ctx.fill();

    // Checkmark inside shield
    ctx.strokeStyle = '#FFFFFF';
    ctx.lineWidth = 2;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.beginPath();
    ctx.moveTo(cx - 6, iconY - 1);
    ctx.lineTo(cx - 2, iconY + 4);
    ctx.lineTo(cx + 7, iconY - 5);
    ctx.stroke();

    // --- Title ---
    const titleY = iconY + 30;
    ctx.fillStyle = TITLE_COLOR;
    ctx.font = 'bold 17px Arial';
    ctx.fillText(t('privacy.title'), cx, titleY);

    // --- Body text ---
    const bodyY = titleY + 28;
    const bodyW = dialogW - 50;
    ctx.fillStyle = TEXT_COLOR;
    ctx.font = '13px Arial';
    ctx.textAlign = 'left';
    ctx.textBaseline = 'top';

    const bodyLines = this._wrapText(ctx, t('privacy.body'), bodyW);
    for (let i = 0; i < bodyLines.length; i++) {
      ctx.fillText(bodyLines[i], dialogX + 25, bodyY + i * 20);
    }

    // --- Privacy policy link ---
    const linkY = bodyY + bodyLines.length * 20 + 10;
    const linkText = t('privacy.policyLink');
    ctx.fillStyle = LINK_COLOR;
    ctx.font = 'bold 13px Arial';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(linkText, cx, linkY);

    // Store link hit area
    const linkMetrics = ctx.measureText(linkText);
    this._policyLink = {
      x: cx - linkMetrics.width / 2 - 6,
      y: linkY - 10,
      w: linkMetrics.width + 12,
      h: 20,
    };

    // Underline the link
    ctx.strokeStyle = LINK_COLOR;
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(cx - linkMetrics.width / 2, linkY + 7);
    ctx.lineTo(cx + linkMetrics.width / 2, linkY + 7);
    ctx.stroke();

    // --- Buttons ---
    const btnAreaY = linkY + 30;
    const btnW = Math.min((dialogW - 60) / 2, 150);
    const btnH = 38;
    const btnGap = 16;
    const agreeX = cx - btnW - btnGap / 2;
    const declineX = cx + btnGap / 2;

    // Agree button
    ctx.fillStyle = AGREE_BG;
    this._roundRect(ctx, agreeX, btnAreaY, btnW, btnH, 8);
    ctx.fill();
    ctx.fillStyle = AGREE_TEXT;
    ctx.font = 'bold 15px Arial';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(t('privacy.agree'), agreeX + btnW / 2, btnAreaY + btnH / 2);
    this._agreeBtn = { x: agreeX, y: btnAreaY, w: btnW, h: btnH };

    // Decline button
    ctx.fillStyle = DECLINE_BG;
    this._roundRect(ctx, declineX, btnAreaY, btnW, btnH, 8);
    ctx.fill();
    ctx.strokeStyle = DECLINE_BORDER;
    ctx.lineWidth = 1;
    this._roundRect(ctx, declineX, btnAreaY, btnW, btnH, 8);
    ctx.stroke();
    ctx.fillStyle = DECLINE_TEXT;
    ctx.font = '14px Arial';
    ctx.fillText(t('privacy.decline'), declineX + btnW / 2, btnAreaY + btnH / 2);
    this._declineBtn = { x: declineX, y: btnAreaY, w: btnW, h: btnH };

    // --- Footer hint ---
    const footerY = btnAreaY + btnH + 16;
    ctx.fillStyle = '#666666';
    ctx.font = '11px Arial';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText(t('privacy.footer'), cx, footerY);
  }

  // ============================================================
  // Private
  // ============================================================

  /**
   * Resolve the privacy authorization request.
   * @param {'agree'|'disagree'} action
   */
  _resolve(action) {
    if (!this._resolveCallback) {
      console.warn('[PrivacyPopup] No resolve callback to call');
      this.hide();
      return;
    }

    try {
      if (action === 'agree') {
        console.log('[PrivacyPopup] Resolving with agree');
        this._resolveCallback({ event: 'agree' });
      } else {
        console.log('[PrivacyPopup] Resolving with disagree');
        // Disagree still needs to resolve, but the API will fail gracefully
        this._resolveCallback({ event: 'disagree' });
      }
    } catch (e) {
      console.error('[PrivacyPopup] resolve() threw:', e);
    }

    this.hide();
  }

  /**
   * Open the privacy policy document.
   * Uses wx.openPrivacyContract if available.
   */
  _openPrivacyPolicy() {
    try {
      if (typeof wx !== 'undefined' && typeof wx.openPrivacyContract === 'function') {
        wx.openPrivacyContract({
          success: () => console.log('[PrivacyPopup] Privacy contract opened'),
          fail: (err) => console.warn('[PrivacyPopup] Failed to open privacy contract:', err),
        });
      } else {
        console.warn('[PrivacyPopup] wx.openPrivacyContract is not available');
      }
    } catch (e) {
      console.warn('[PrivacyPopup] Error opening privacy contract:', e);
    }
  }

  /**
   * Simple hit-test for rectangular area.
   */
  _hitTest(tx, ty, rect) {
    return tx >= rect.x && tx <= rect.x + rect.w &&
           ty >= rect.y && ty <= rect.y + rect.h;
  }

  /**
   * Wrap text into lines that fit within maxWidth.
   */
  _wrapText(ctx, text, maxWidth) {
    const lines = [];
    const paragraphs = text.split('\n');
    for (const para of paragraphs) {
      let line = '';
      for (let i = 0; i < para.length; i++) {
        const testLine = line + para[i];
        const metrics = ctx.measureText(testLine);
        if (metrics.width > maxWidth && line.length > 0) {
          lines.push(line);
          line = para[i];
        } else {
          line = testLine;
        }
      }
      if (line) lines.push(line);
    }
    return lines;
  }

  /**
   * Draw a rounded rectangle path.
   */
  _roundRect(ctx, x, y, w, h, r) {
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.arcTo(x + w, y, x + w, y + r, r);
    ctx.lineTo(x + w, y + h - r);
    ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
    ctx.lineTo(x + r, y + h);
    ctx.arcTo(x, y + h, x, y + h - r, r);
    ctx.lineTo(x, y + r);
    ctx.arcTo(x, y, x + r, y, r);
    ctx.closePath();
  }
}

module.exports = PrivacyPopup;
