const express = require('express');
const { missionHelpers, missionRollHelpers, logToFile } = require('../mariadb');
const { battlemapVisibleForPlayer, buildBattlemapStateForScene } = require('../../src/shared/battlemapEngine');
const router = express.Router();

function ensureMissionBattlemaps(missionData = {}) {
  const scenes = Array.isArray(missionData.scenes) ? missionData.scenes : [];
  let changed = false;
  const nextScenes = scenes.map((scene) => {
    if (!scene || typeof scene !== 'object') return scene;
    const nextBattlemapState = buildBattlemapStateForScene(scene, scene.battlemapState || {});
    const sceneChanged = JSON.stringify(scene.battlemapState || null) !== JSON.stringify(nextBattlemapState);
    if (sceneChanged) changed = true;
    return {
      ...scene,
      battlemapState: nextBattlemapState,
    };
  });
  return {
    changed,
    mission: {
      ...missionData,
      scenes: nextScenes,
    },
  };
}

async function persistMissionBattlemapsIfNeeded(mission) {
  if (!mission?.id) return mission;
  const normalized = ensureMissionBattlemaps(mission);
  if (!normalized.changed) return normalized.mission;
  await missionHelpers.updateProgress(mission.id, {
    currentScene: Number.isInteger(mission.current_scene) ? mission.current_scene : 0,
    scenes: normalized.mission.scenes,
  });
  return normalized.mission;
}

function playerScene(scene) {
  if (!scene || typeof scene !== 'object') return null;
  const {
    gmNotes,
    gm_notes,
    secret,
    secrets,
    hidden,
    gmOnly,
    gm_only,
    endingText,
    ending_text,
    outro,
    completionText,
    storyHook,
    plotThread,
    ...safeScene
  } = scene;
  if (!safeScene.skillConditionsUnlocked) {
    delete safeScene.skillDifficultyModifier;
    delete safeScene.skillSituationModifiers;
    delete safeScene.skillCustomModifier;
  }
  if (!battlemapVisibleForPlayer(scene, 'player')) {
    delete safeScene.battlemapState;
  }
  return safeScene;
}

function titleForScene(scene, index) {
  return scene?.title || scene?.name || `Scene ${index + 1}`;
}

function compactText(value) {
  if (Array.isArray(value)) return value.map(compactText).filter(Boolean).join(' ');
  if (value && typeof value === 'object') return Object.values(value).map(compactText).filter(Boolean).join(' ');
  return String(value || '').replace(/\s+/g, ' ').trim();
}

function defaultStoryHook(scene, index, scenes, mission = {}) {
  const title = titleForScene(scene, index);
  const next = scenes[index + 1];
  const nextTitle = next ? titleForScene(next, index + 1) : 'the final debrief';
  const theme = compactText(mission.theme || mission.threatLevel || mission.threat_level || 'mission threat');
  const existingReward = Array.isArray(scene?.checks)
    ? scene.checks.map(check => compactText(check.reward)).find(Boolean)
    : '';
  const clue = existingReward || `Evidence in ${title} ties this scene to the main ${theme} story.`;
  return {
    thread: `Main thread for ${mission.name || 'the mission'}`,
    clue,
    secret: compactText(scene?.secret || scene?.secrets || scene?.hidden) || `The important clue in ${title} points to ${nextTitle}.`,
    transition: next ? `Use this discovery to point the kill-team toward ${nextTitle}.` : 'Use this discovery to frame the mission result and the next assignment.',
    gmAction: 'Reveal the clue on a successful scene skill check; on failure, reveal a partial clue with cost so the mission keeps moving.',
    failForward: 'Failure should add pressure, casualties, lost time, enemy preparation, or resource cost, not stop the story.',
  };
}

function ensureSceneStoryHooks(missionData = {}) {
  const scenes = Array.isArray(missionData.scenes) ? missionData.scenes : [];
  if (!scenes.length) return missionData;
  return {
    ...missionData,
    scenes: scenes.map((scene, index) => {
      if (!scene || typeof scene !== 'object') return scene;
      const storyHook = scene.storyHook && typeof scene.storyHook === 'object'
        ? scene.storyHook
        : defaultStoryHook(scene, index, scenes, missionData);
      const checks = Array.isArray(scene.checks) && scene.checks.length
        ? scene.checks.map((check, checkIndex) => {
            if (check.reward) return check;
            const reward = checkIndex === 0 ? storyHook.clue : storyHook.transition;
            return { ...check, reward };
          })
        : scene.checks;
      return {
        ...scene,
        storyHook,
        secret: scene.secret || storyHook.secret,
        gmNotes: scene.gmNotes || scene.gm_notes || `${storyHook.gmAction} ${storyHook.failForward}`,
        checks,
      };
    }),
  };
}

// Get all missions
router.get('/', async (req, res) => {
  try {
    const missions = await missionHelpers.getAll();
    const normalized = missions.map(mission => ensureMissionBattlemaps(mission).mission);
    res.json(normalized);
  } catch (error) {
    logToFile('API: Failed to get missions', error);
    res.status(500).json({ error: String(error) });
  }
});

// Get active mission for the shared play table
router.get('/active/current', async (req, res) => {
  try {
    const mission = await missionHelpers.getActive();
    const normalized = mission ? await persistMissionBattlemapsIfNeeded(mission) : null;
    res.json(normalized || null);
  } catch (error) {
    logToFile('API: Failed to get active mission', error);
    res.status(500).json({ error: String(error) });
  }
});

// Get player-safe active mission. Only the GM-current scene is returned.
router.get('/active/player', async (req, res) => {
  try {
    const mission = await missionHelpers.getActive();
    if (!mission) return res.json(null);
    const normalizedMission = await persistMissionBattlemapsIfNeeded(mission);

    const scenes = Array.isArray(normalizedMission.scenes) ? normalizedMission.scenes : [];
    const currentIndex = Math.max(0, Math.min(Number(normalizedMission.current_scene || 0), Math.max(scenes.length - 1, 0)));
    const currentScene = scenes[currentIndex] || null;

    res.json({
      id: normalizedMission.id,
      name: normalizedMission.name,
      current_scene: 0,
      active_scene_index: currentIndex,
      scenes: currentScene ? [playerScene(currentScene)] : [],
    });
  } catch (error) {
    logToFile('API: Failed to get player active mission', error);
    res.status(500).json({ error: String(error) });
  }
});

// Get roll feed for the active mission
router.get('/active/rolls/feed', async (req, res) => {
  try {
    const rolls = await missionRollHelpers.getActive(Number(req.query.limit || 50));
    res.json(rolls);
  } catch (error) {
    logToFile('API: Failed to get active mission rolls', error);
    res.status(500).json({ error: String(error) });
  }
});

// Record a roll for GM visibility
router.post('/rolls', async (req, res) => {
  try {
    const id = await missionRollHelpers.create(req.body || {});
    if (!id) return res.status(500).json({ error: 'Failed to record roll' });
    res.json({ success: true, id });
  } catch (error) {
    logToFile('API: Failed to record mission roll', error);
    res.status(500).json({ error: String(error) });
  }
});

// Patch only the active scene without replacing the full scenes array.
// This is used by player-facing mini rollers, because /active/player only
// returns the current scene rather than the complete mission.
router.put('/active/scene', async (req, res) => {
  try {
    const mission = await missionHelpers.getActive();
    if (!mission) return res.status(404).json({ error: 'No active mission' });
    const normalizedMission = ensureMissionBattlemaps(mission).mission;
    const scenes = Array.isArray(normalizedMission.scenes) ? normalizedMission.scenes : [];
    const currentIndex = Math.max(0, Math.min(Number(normalizedMission.current_scene || 0), Math.max(scenes.length - 1, 0)));
    const currentScene = scenes[currentIndex] || {};
    const patch = req.body?.patch || {};
    const nextScenes = scenes.map((scene, index) => (
      index === currentIndex ? { ...currentScene, ...patch } : scene
    ));
    const ensuredMission = ensureMissionBattlemaps({ ...normalizedMission, scenes: nextScenes }).mission;
    const ok = await missionHelpers.updateProgress(mission.id, {
      currentScene: currentIndex,
      scenes: ensuredMission.scenes,
    });
    if (!ok) return res.status(500).json({ error: 'Failed to patch active scene' });
    const updated = await missionHelpers.getById(mission.id);
    res.json({ success: true, mission: updated, scene: ensuredMission.scenes[currentIndex], currentScene: currentIndex });
  } catch (error) {
    logToFile('API: Failed to patch active scene', error);
    res.status(500).json({ error: String(error) });
  }
});

// Delete a roll from the mission feed
router.delete('/rolls/:id', async (req, res) => {
  try {
    const ok = await missionRollHelpers.delete(req.params.id);
    if (!ok) return res.status(404).json({ error: 'Roll not found' });
    res.json({ success: true });
  } catch (error) {
    logToFile('API: Failed to delete mission roll', req.params.id, error);
    res.status(500).json({ error: String(error) });
  }
});

// Clear all rolls for the active mission feed
router.delete('/rolls', async (req, res) => {
  try {
    const deleted = await missionRollHelpers.clearActive();
    if (deleted === null) return res.status(404).json({ error: 'No active mission' });
    res.json({ success: true, deleted });
  } catch (error) {
    logToFile('API: Failed to clear active mission rolls', error);
    res.status(500).json({ error: String(error) });
  }
});

// Get mission by ID
router.get('/:id', async (req, res) => {
  try {
    const mission = await missionHelpers.getById(req.params.id);
    if (!mission) return res.status(404).json({ error: 'Mission not found' });
    const normalized = await persistMissionBattlemapsIfNeeded(mission);
    res.json(normalized);
  } catch (error) {
    logToFile('API: Failed to get mission', req.params.id, error);
    res.status(500).json({ error: String(error) });
  }
});

// Create mission
router.post('/', async (req, res) => {
  try {
    const missionData = ensureMissionBattlemaps(ensureSceneStoryHooks(req.body || {})).mission;
    const id = await missionHelpers.create(missionData);
    if (!id) return res.status(500).json({ error: 'Failed to create mission' });
    logToFile('API: Created mission', missionData.name);
    res.json({ success: true, id });
  } catch (error) {
    logToFile('API: Failed to create mission', error);
    res.status(500).json({ error: String(error) });
  }
});

// Set active mission
router.post('/:id/active', async (req, res) => {
  try {
    const ok = await missionHelpers.setActive(req.params.id);
    if (!ok) return res.status(404).json({ error: 'Mission not found' });
    const mission = await missionHelpers.getById(req.params.id);
    const normalized = mission ? await persistMissionBattlemapsIfNeeded(mission) : null;
    res.json({ success: true, mission: normalized });
  } catch (error) {
    logToFile('API: Failed to set active mission', req.params.id, error);
    res.status(500).json({ error: String(error) });
  }
});

// Update active play progress
router.put('/:id/progress', async (req, res) => {
  try {
    const progressData = ensureMissionBattlemaps(ensureSceneStoryHooks(req.body || {})).mission;
    const ok = await missionHelpers.updateProgress(req.params.id, {
      currentScene: Number.isInteger(progressData.currentScene) ? progressData.currentScene : Number(progressData.current_scene || 0),
      scenes: progressData.scenes,
    });
    if (!ok) return res.status(404).json({ error: 'Mission not found' });
    const mission = await missionHelpers.getById(req.params.id);
    res.json({ success: true, mission: mission ? ensureMissionBattlemaps(mission).mission : null });
  } catch (error) {
    logToFile('API: Failed to update mission progress', req.params.id, error);
    res.status(500).json({ error: String(error) });
  }
});

// Update mission
router.put('/:id', async (req, res) => {
  try {
    const missionData = ensureMissionBattlemaps(ensureSceneStoryHooks(req.body || {})).mission;
    const ok = await missionHelpers.update(req.params.id, missionData);
    if (!ok) return res.status(404).json({ error: 'Mission not found' });
    const mission = await missionHelpers.getById(req.params.id);
    res.json({ success: true, mission: mission ? ensureMissionBattlemaps(mission).mission : null });
  } catch (error) {
    logToFile('API: Failed to update mission', req.params.id, error);
    res.status(500).json({ error: String(error) });
  }
});

// Delete mission
router.delete('/:id', async (req, res) => {
  try {
    const ok = await missionHelpers.delete(req.params.id);
    if (!ok) return res.status(404).json({ error: 'Mission not found' });
    res.json({ success: true });
  } catch (error) {
    logToFile('API: Failed to delete mission', req.params.id, error);
    res.status(500).json({ error: String(error) });
  }
});

console.log('Mission routes registered (MariaDB)');
module.exports = router;
