import './style.css';
import { createState, tick, buyUpgrade, visitLocation, resolveEventChoice, startGame, abandonGame, saveState, loadState, clearSave, applyOfflineProgress, VISITABLE_LOCATION_IDS } from './engine.ts';
import { initUI, renderState, setPausedUI } from './ui.ts';
import { initMap, updateMap } from './map.ts';

let state = loadState() ?? createState();

// The cult doesn't pause just because the tab isn't in front of you —
// catch up on what happened while you were gone, once, on load.
if (state.phase === 'playing') {
  state = applyOfflineProgress(state, Date.now() - (state.lastRealTime ?? Date.now()));
}

let visitCooldowns = new Map<string, number>();
let paused = false;
let lastTime = 0;
const TARGET_FPS = 30;
const FRAME_MS = 1000 / TARGET_FPS;
const SAVE_INTERVAL_MS = 10_000;
let lastSaveTime = 0;

const WHISPER_INTERVAL_TICKS = 30 * 45; // 45s at 30fps
const WHISPER_RETRY_TICKS = 30 * 5;     // retry sooner if every location was on cooldown
let nextWhisperTick = 0;

const BASE_TITLE = document.title;
const HAUNT_MESSAGES = ['Han väntar…', 'Vakna honom…', 'Havet kallar…', 'Kom tillbaka…', '☿ Källaren minns ☿'];
let hauntTimer: number | undefined;
let hauntIndex = 0;

document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    if (state.phase !== 'playing') return;
    hauntIndex = 0;
    hauntTimer = window.setInterval(() => {
      document.title = HAUNT_MESSAGES[hauntIndex % HAUNT_MESSAGES.length]!;
      hauntIndex += 1;
    }, 2500);
  } else {
    if (hauntTimer !== undefined) { window.clearInterval(hauntTimer); hauntTimer = undefined; }
    document.title = BASE_TITLE;
    // Coming back to the tab is itself a kind of "return" — re-check for
    // offline progress in case the tab was merely backgrounded, not
    // reloaded (requestAnimationFrame throttles hard in hidden tabs, so
    // real time can drift a long way ahead of simulated game time).
    if (state.phase === 'playing') {
      state = applyOfflineProgress(state, Date.now() - (state.lastRealTime ?? Date.now()));
      state = { ...state, lastRealTime: Date.now() };
      renderState(state);
    }
  }
});

function start(): void {
  const app = document.getElementById('app')!;

  initUI(app,
    (id) => { state = buyUpgrade(state, id); renderState(state); saveState(state); },
    () => {
      clearSave();
      state = createState();
      visitCooldowns = new Map();
      document.getElementById('overlay')!.classList.remove('visible');
      renderState(state);
    },
    (index) => { state = resolveEventChoice(state, index); renderState(state); saveState(state); },
    () => { state = startGame(state); renderState(state); saveState(state); },
    () => { state = abandonGame(state); renderState(state); saveState(state); },
    () => { paused = !paused; setPausedUI(paused); }
  );

  initMap('map', (locationId) => {
    const result = visitLocation(state, locationId, visitCooldowns);
    state = result.state;
    visitCooldowns = result.cooldowns;
    renderState(state);
  });
  renderState(state);

  requestAnimationFrame(loop);
}

function loop(now: number): void {
  if (now - lastTime >= FRAME_MS) {
    lastTime = now;
    if (state.phase === 'playing' && !paused) {
      state = tick(state);

      // "Viskningen leder er" — once purchased, the cult moves on its own
      // occasionally without a click. Skip a location currently on
      // cooldown rather than burning the whole interval on a no-op visit.
      if (state.upgrades.has('whisper_guidance') && state.tick >= nextWhisperTick) {
        const tickNow = state.tick;
        const available = VISITABLE_LOCATION_IDS.filter(id => (visitCooldowns.get(id) ?? 0) <= tickNow);
        if (available.length) {
          const id = available[Math.floor(Math.random() * available.length)]!;
          const result = visitLocation(state, id, visitCooldowns);
          state = result.state;
          visitCooldowns = result.cooldowns;
          nextWhisperTick = state.tick + WHISPER_INTERVAL_TICKS;
        } else {
          nextWhisperTick = state.tick + WHISPER_RETRY_TICKS;
        }
      }

      renderState(state);
      updateMap(state.dread, state.areas);
    }
    if (now - lastSaveTime >= SAVE_INTERVAL_MS) {
      lastSaveTime = now;
      state = { ...state, lastRealTime: Date.now() };
      saveState(state);
    }
  }
  requestAnimationFrame(loop);
}

start();
