/**
 * game.js
 * Entry point for Tank War WeChat mini game.
 * Initializes canvas, sets up the game loop, and manages scene lifecycle.
 */

// Ensure timer globals exist (some WeChat base library versions may not
// inject them early enough, causing WAGame.js internal error-reporting to
// crash with "Can't find variable: setTimeout").
if (typeof setTimeout === 'undefined') {
  GameGlobal.setTimeout = GameGlobal.setTimeout || function (fn, ms) {
    // Fallback: execute synchronously when real timer is unavailable
    fn();
  };
  GameGlobal.setInterval = GameGlobal.setInterval || function () {};
  GameGlobal.clearTimeout = GameGlobal.clearTimeout || function () {};
  GameGlobal.clearInterval = GameGlobal.clearInterval || function () {};
}

const SceneManager = require('./js/managers/SceneManager');
const ResourceManager = require('./js/managers/ResourceManager');
const StorageManager = require('./js/managers/StorageManager');
const AudioManager = require('./js/managers/AudioManager');
const NetworkManager = require('./js/managers/NetworkManager');
const AdManager = require('./js/managers/AdManager');
const ShareManager = require('./js/managers/ShareManager');
const CurrencyManager = require('./js/managers/CurrencyManager');
const PaymentManager = require('./js/managers/PaymentManager');
const ComplianceManager = require('./js/managers/ComplianceManager');
const ContentSecurityManager = require('./js/managers/ContentSecurityManager');
const BuffManager = require('./js/managers/BuffManager');
const SkinManager = require('./js/managers/SkinManager');
const PlayerProfile = require('./js/managers/PlayerProfile');
const PrivacyPopup = require('./js/ui/PrivacyPopup');
const EventBus = require('./js/base/EventBus');
const {
  SCREEN_WIDTH,
  SCREEN_HEIGHT,
  DEVICE_PIXEL_RATIO,
  COLORS,
  SCENE,
} = require('./js/base/GameGlobal');

// ============================================================
// Canvas Setup
// ============================================================
const canvas = wx.createCanvas();
const ctx = canvas.getContext('2d');

// Handle high-DPI screens
canvas.width = SCREEN_WIDTH * DEVICE_PIXEL_RATIO;
canvas.height = SCREEN_HEIGHT * DEVICE_PIXEL_RATIO;
ctx.scale(DEVICE_PIXEL_RATIO, DEVICE_PIXEL_RATIO);

// ============================================================
// Core Singletons
// ============================================================
const sceneManager = new SceneManager();
const resourceManager = new ResourceManager();
const storageManager = new StorageManager();
const audioManager = new AudioManager();
const networkManager = new NetworkManager();
const eventBus = new EventBus();

// Expose globally so all modules can access
GameGlobal.canvas = canvas;
GameGlobal.ctx = ctx;
GameGlobal.sceneManager = sceneManager;
GameGlobal.resourceManager = resourceManager;
GameGlobal.storageManager = storageManager;
GameGlobal.audioManager = audioManager;
GameGlobal.networkManager = networkManager;
GameGlobal.eventBus = eventBus;

// Initialize ad and share managers (depend on storageManager being set)
const adManager = new AdManager();
const shareManager = new ShareManager();
const currencyManager = new CurrencyManager();
const paymentManager = new PaymentManager();
const complianceManager = new ComplianceManager();
const contentSecurityManager = new ContentSecurityManager();
const buffManager = new BuffManager();
const skinManager = new SkinManager();
const playerProfile = new PlayerProfile();
const privacyPopup = new PrivacyPopup();
GameGlobal.adManager = adManager;
GameGlobal.shareManager = shareManager;
GameGlobal.currencyManager = currencyManager;
GameGlobal.paymentManager = paymentManager;
GameGlobal.complianceManager = complianceManager;
GameGlobal.contentSecurityManager = contentSecurityManager;
GameGlobal.buffManager = buffManager;
GameGlobal.skinManager = skinManager;
GameGlobal.playerProfile = playerProfile;
GameGlobal.privacyPopup = privacyPopup;

// ============================================================
// Game State
// ============================================================
let isPaused = false;
let lastTimestamp = 0;

// ============================================================
// Touch Event Forwarding
// ============================================================
wx.onTouchStart((e) => {
  // Privacy popup consumes all touches while active
  if (privacyPopup.active) {
    privacyPopup.handleTouch('touchstart', e);
    return;
  }
  sceneManager.handleTouch('touchstart', e);
});
wx.onTouchMove((e) => {
  if (privacyPopup.active) return;
  sceneManager.handleTouch('touchmove', e);
});
wx.onTouchEnd((e) => {
  if (privacyPopup.active) {
    privacyPopup.handleTouch('touchend', e);
    return;
  }
  sceneManager.handleTouch('touchend', e);
});

// ============================================================
// WeChat Privacy Authorization (required since base library 2.32.3)
// ============================================================
if (typeof wx !== 'undefined' && typeof wx.onNeedPrivacyAuthorization === 'function') {
  // Once the user has explicitly agreed to privacy, we auto-resolve any
  // subsequent onNeedPrivacyAuthorization callbacks without showing the
  // popup again. This prevents showing duplicate popups.
  let _privacyAgreed = false;

  wx.onNeedPrivacyAuthorization((resolve, eventInfo) => {
    console.log('[game.js] onNeedPrivacyAuthorization triggered, eventInfo:', eventInfo, ', alreadyAgreed:', _privacyAgreed);

    // If the user already agreed, auto-resolve without showing the popup.
    if (_privacyAgreed) {
      console.log('[game.js] Auto-resolving privacy (already agreed)');
      resolve({ event: 'agree' });
      return;
    }

    // Show the privacy popup for the first-time authorization.
    // When the user taps "Agree" or "Decline", the popup calls resolve().
    const wrappedResolve = (result) => {
      console.log('[game.js] Privacy resolved with:', JSON.stringify(result));
      resolve(result);

      // Remember that the user agreed — future triggers auto-resolve.
      if (result && result.event === 'agree') {
        _privacyAgreed = true;
      }
    };

    privacyPopup.show(wrappedResolve, eventInfo);
  });
}

// ============================================================
// Lifecycle: pause / resume on background switch
// ============================================================
wx.onHide(() => {
  isPaused = true;
  audioManager.pauseAll();
  eventBus.emit('game:pause');
});

wx.onShow((res) => {
  isPaused = false;
  lastTimestamp = 0; // reset so dt doesn't spike
  audioManager.resumeAll();
  eventBus.emit('game:resume');

  console.log(`[game.js] onShow fired: scene=${res && res.scene}, query=${JSON.stringify(res && res.query)}, shareTicket=${res && res.shareTicket}`);

  // Check for teamId from invite card (3v3 mode) or roomId from invite card (1v1 mode)
  const teamId = _extractTeamId(res && res.query);
  const teamMode = _extractTeamMode(res && res.query);
  const roomId = _extractRoomId(res && res.query);
  if (teamId) {
    _handleInviteTeamId(teamId, teamMode);
  } else if (roomId) {
    _handleInviteRoomId(roomId);
  } else {
    // Fallback: also check launch options in case onShow query is empty on cold start
    try {
      const launchOptions = wx.getLaunchOptionsSync();
      const fallbackTeamId = _extractTeamId(launchOptions && launchOptions.query);
      const fallbackTeamMode = _extractTeamMode(launchOptions && launchOptions.query);
      const fallbackRoomId = _extractRoomId(launchOptions && launchOptions.query);
      if (fallbackTeamId) {
        console.log(`[game.js] onShow query empty, but found teamId in launchOptions: ${fallbackTeamId}`);
        _handleInviteTeamId(fallbackTeamId, fallbackTeamMode);
      } else if (fallbackRoomId) {
        console.log(`[game.js] onShow query empty, but found roomId in launchOptions: ${fallbackRoomId}`);
        _handleInviteRoomId(fallbackRoomId);
      }
    } catch (e) {}
  }
});

/**
 * Extract teamId from query parameter.
 * WeChat may provide query as an Object ({teamId: 'xxx'}) or as a raw
 * query-string ('teamId=xxx'). This helper handles both formats.
 * @param {object|string|undefined} query
 * @returns {string|null}
 */
function _extractTeamId(query) {
  if (!query) return null;

  // Log the raw query for debugging
  try {
    console.log(`[game.js] _extractTeamId raw query: ${JSON.stringify(query)}, type: ${typeof query}`);
  } catch (e) {
    console.log(`[game.js] _extractTeamId query type: ${typeof query}`);
  }

  // Case 1: query is already an object with teamId property
  if (typeof query === 'object' && query.teamId) {
    return query.teamId;
  }

  // Case 2: query is a string like 'teamId=T12345' or 'teamId=T12345&foo=bar'
  if (typeof query === 'string') {
    const match = query.match(/teamId=([^&]+)/);
    if (match) return match[1];
  }

  // Case 3: query is an object but teamId might be nested in a raw string field
  // Some WeChat versions put the whole query string in a single property
  if (typeof query === 'object') {
    const keys = Object.keys(query);
    // If the object has a single key that looks like a query string
    for (const key of keys) {
      const combined = key + (query[key] ? '=' + query[key] : '');
      const match = combined.match(/teamId=([^&]+)/);
      if (match) return match[1];
    }
  }

  return null;
}

/**
 * Extract mode parameter from query (e.g. mode=2v2 or mode=3v3).
 * Used to route team invites to the correct room scene.
 * @param {object|string|undefined} query
 * @returns {string|null}
 */
function _extractTeamMode(query) {
  if (!query) return null;

  if (typeof query === 'object' && query.mode) {
    return query.mode;
  }

  if (typeof query === 'string') {
    const match = query.match(/mode=([^&]+)/);
    if (match) return match[1];
  }

  if (typeof query === 'object') {
    const keys = Object.keys(query);
    for (const key of keys) {
      const combined = key + (query[key] ? '=' + query[key] : '');
      const match = combined.match(/mode=([^&]+)/);
      if (match) return match[1];
    }
  }

  return null;
}

/**
 * Extract roomId from query parameter (1v1 invite card).
 * Similar to _extractTeamId, but looks for roomId key.
 * @param {object|string|undefined} query
 * @returns {string|null}
 */
function _extractRoomId(query) {
  if (!query) return null;

  // Case 1: query is already an object with roomId property
  if (typeof query === 'object' && query.roomId) {
    return query.roomId;
  }

  // Case 2: query is a string like 'roomId=R12345' or 'roomId=R12345&foo=bar'
  if (typeof query === 'string') {
    const match = query.match(/roomId=([^&]+)/);
    if (match) return match[1];
  }

  // Case 3: query is an object but roomId might be nested in a raw string field
  if (typeof query === 'object') {
    const keys = Object.keys(query);
    for (const key of keys) {
      const combined = key + (query[key] ? '=' + query[key] : '');
      const match = combined.match(/roomId=([^&]+)/);
      if (match) return match[1];
    }
  }

  return null;
}

/**
 * Handle roomId from 1v1 invite card.
 * Navigates to RoomScene (auto-join) if possible, otherwise stores as pending.
 * @param {string} roomId
 */
function _handleInviteRoomId(roomId) {
  if (!roomId) return;

  // Avoid duplicate processing if already pending the same roomId
  if (GameGlobal._pendingRoomId === roomId) {
    console.log(`[game.js] roomId ${roomId} already pending, skipping duplicate`);
    return;
  }

  console.log(`[game.js] Received roomId from 1v1 invite: ${roomId}, currentScene: ${sceneManager._currentName}`);

  // If already past loading, navigate directly to PVP room scene
  if (sceneManager._currentScene && sceneManager._currentName !== SCENE.LOADING) {
    console.log(`[game.js] Navigating directly to RoomScene with roomId: ${roomId}`);
    if (!sceneManager._scenes.has(SCENE.PVP_ROOM)) {
      const RoomScene = require('./js/scenes/RoomScene');
      sceneManager.register(SCENE.PVP_ROOM, RoomScene);
    }
    sceneManager.switchTo(SCENE.PVP_ROOM, { roomId });
    GameGlobal._pendingRoomId = null;
  } else {
    // Still loading — store pending roomId for auto-navigation after load
    console.log(`[game.js] Still loading, storing pendingRoomId: ${roomId}`);
    GameGlobal._pendingRoomId = roomId;
  }
}

/**
 * Handle teamId from invite card (shared between onShow and cold launch).
 * Routes to Team2v2RoomScene or TeamRoomScene based on mode parameter.
 * @param {string} teamId
 * @param {string|null} mode - '2v2' or '3v3' (default: '3v3')
 */
function _handleInviteTeamId(teamId, mode) {
  if (!teamId) return;

  // Avoid duplicate processing if already pending the same teamId
  if (GameGlobal._pendingTeamId === teamId) {
    console.log(`[game.js] teamId ${teamId} already pending, skipping duplicate`);
    return;
  }

  const is2v2 = mode === '2v2';
  const targetScene = is2v2 ? SCENE.TEAM_2V2_ROOM : SCENE.TEAM_ROOM;
  const sceneName = is2v2 ? 'Team2v2RoomScene' : 'TeamRoomScene';

  console.log(`[game.js] Received teamId from invite: ${teamId}, mode: ${mode || '3v3'}, targetScene: ${targetScene}, currentScene: ${sceneManager._currentName}`);

  // If already past loading, navigate directly to the team room
  if (sceneManager._currentScene && sceneManager._currentName !== SCENE.LOADING) {
    console.log(`[game.js] Navigating directly to ${sceneName} with teamId: ${teamId}`);
    if (!sceneManager._scenes.has(targetScene)) {
      const SceneModule = is2v2
        ? require('./js/scenes/Team2v2RoomScene')
        : require('./js/scenes/TeamRoomScene');
      sceneManager.register(targetScene, SceneModule);
    }
    sceneManager.switchTo(targetScene, { teamId });
    GameGlobal._pendingTeamId = null;
    GameGlobal._pendingTeamMode = null;
  } else {
    // Still loading — store pending teamId for auto-navigation after load
    console.log(`[game.js] Still loading, storing pendingTeamId: ${teamId}, mode: ${mode || '3v3'}`);
    GameGlobal._pendingTeamId = teamId;
    GameGlobal._pendingTeamMode = mode || null;
  }
}

// Check for teamId / roomId from cold launch (user opened game via invite card)
try {
  const launchOptions = wx.getLaunchOptionsSync();
  console.log(`[game.js] Cold launch options: scene=${launchOptions.scene}, query=${JSON.stringify(launchOptions.query)}, referrerInfo=${JSON.stringify(launchOptions.referrerInfo)}`);
  const launchTeamId = _extractTeamId(launchOptions && launchOptions.query);
  const launchTeamMode = _extractTeamMode(launchOptions && launchOptions.query);
  const launchRoomId = _extractRoomId(launchOptions && launchOptions.query);
  if (launchTeamId) {
    _handleInviteTeamId(launchTeamId, launchTeamMode);
  } else if (launchRoomId) {
    _handleInviteRoomId(launchRoomId);
  } else {
    console.log('[game.js] No teamId/roomId found in cold launch options');
  }
} catch (e) {
  console.error('[game.js] getLaunchOptionsSync failed:', e);
}

// ============================================================
// Game Loop
// ============================================================
function gameLoop(timestamp) {
  requestAnimationFrame(gameLoop);

  if (isPaused) return;

  // Calculate delta time in seconds, cap at 100ms to avoid spiral
  if (lastTimestamp === 0) lastTimestamp = timestamp;
  let dt = (timestamp - lastTimestamp) / 1000;
  if (dt > 0.1) dt = 0.1;
  lastTimestamp = timestamp;

  // Clear screen
  ctx.clearRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
  ctx.fillStyle = COLORS.BG;
  ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

  // Update & render current scene
  sceneManager.update(dt);
  sceneManager.render(ctx);

  // Privacy popup renders on top of everything
  if (privacyPopup.active) {
    privacyPopup.render(ctx);
  }
}

// ============================================================
// Loading Scene (inline, minimal)
// ============================================================
const LoadingScene = {
  _progress: 0,

  enter() {
    this._progress = 0;
    this._startLoading();
  },

  exit() {},

  async _startLoading() {
    // Initialize audio system (programmatic synthesis, no files needed)
    audioManager.init();

    // Initialize content security manager (local word list + remote sync)
    // Derive HTTP base URL from WebSocket server URL for content security API calls
    const wsUrl = networkManager._serverUrl || '';
    const httpServerUrl = wsUrl.replace(/^wss?/, 'https');
    contentSecurityManager.init({
      serverUrl: httpServerUrl,
    });

    // Define all image assets to preload
    // For now we use procedural drawing, so asset list is empty.
    // Assets can be added later as the game grows.
    const assets = [];

    if (assets.length === 0) {
      // No assets to load, go directly to menu
      this._progress = 1;
      // Use setTimeout to allow at least one render frame of loading screen
      setTimeout(() => {
        const MenuScene = require('./js/scenes/MenuScene');
        sceneManager.register(SCENE.MENU, MenuScene);

        // Register other scenes lazily as they are created
        sceneManager.switchTo(SCENE.MENU);
      }, 300);
      return;
    }

    await resourceManager.loadImages(assets, (loaded, total) => {
      this._progress = loaded / total;
    });

    const MenuScene = require('./js/scenes/MenuScene');
    sceneManager.register(SCENE.MENU, MenuScene);
    sceneManager.switchTo(SCENE.MENU);
  },

  update(dt) {},

  render(ctx) {
    // Draw loading screen
    const cx = SCREEN_WIDTH / 2;
    const cy = SCREEN_HEIGHT / 2;

    // Title
    ctx.fillStyle = COLORS.MENU_TITLE;
    ctx.font = 'bold 28px Arial';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText('坦克探险', cx, cy - 60);

    // Progress bar background
    const barW = SCREEN_WIDTH * 0.6;
    const barH = 12;
    const barX = cx - barW / 2;
    const barY = cy;
    ctx.fillStyle = '#333333';
    ctx.fillRect(barX, barY, barW, barH);

    // Progress bar fill
    ctx.fillStyle = COLORS.MENU_TITLE;
    ctx.fillRect(barX, barY, barW * this._progress, barH);

    // Loading text
    ctx.fillStyle = COLORS.HUD_TEXT;
    ctx.font = '14px Arial';
    ctx.fillText(`加载中... ${Math.floor(this._progress * 100)}%`, cx, cy + 30);
  },

  handleTouch() {},
};

// ============================================================
// Bootstrap
// ============================================================
sceneManager.register(SCENE.LOADING, LoadingScene);
sceneManager.switchTo(SCENE.LOADING);

// Start the game loop
requestAnimationFrame(gameLoop);
