import type { State } from './engine.ts';
import { UPGRADES } from './upgrades.ts';
import { VERSION, CHANGELOG } from './data/version.ts';
import { CHARACTERS } from './characters.ts';

export function initUI(
  app: HTMLElement,
  onUpgrade: (id: string) => void,
  onRestart: () => void,
  onEventChoice: (index: number) => void,
  onStart: () => void,
  onAbandon: () => void,
  onTogglePause: () => void,
): void {
  app.innerHTML = `
    <div id="header">
      <div class="brand">DJUPET</div>
      <div class="brand-sub">Malmö, Sverige · <span id="phase-tag" class="phase-tag early">Tidigt skede</span><span id="pause-tag" class="pause-tag"></span></div>
      <div class="header-actions">
        <button id="pause-btn" class="help-btn" title="Pausa spelet">Pausa</button>
        <button id="characters-btn" class="help-btn" title="Sällskapet">Sällskapet</button>
        <button id="abandon-btn" class="help-btn" title="Överge kulten">Avbryt</button>
        <button id="help-btn" class="help-btn" title="Hjälp &amp; Ändringslogg">? v${VERSION}</button>
      </div>
    </div>

    <div id="intro-screen" style="background-image:url('/img/intro.webp')">
      <div class="intro-scrim">
        <div class="intro-title">DJUPET</div>
        <div class="intro-sub">Malmö, Sverige</div>
        <p class="intro-flavor">
          Nio stolar i en fuktig källare i Rosengård. En av dem är alltid lite för varm.<br>
          Ni har hört sången i väggarna. Den Sovande har inte hört er ännu.<br>
          Det är dags att ändra på det.
        </p>
        <button class="overlay-btn" id="start-btn">Börja ritualen</button>
      </div>
    </div>

    <div id="help-modal" class="help-modal">
      <div class="help-modal-inner" id="help-modal-inner">
        <div class="help-modal-header">
          <span class="help-modal-title">DJUPET — Hjälp &amp; Ändringslogg</span>
          <button id="help-close" class="help-close">✕</button>
        </div>
        <div class="help-modal-body">
          <section class="help-section">
            <div class="help-section-title">Hur man spelar</div>
            <ul class="help-list">
              <li><strong>Andakt</strong> ackumuleras passivt. Använd den för att köpa åtgärder i vänsterpanelen.</li>
              <li><strong>Dread</strong> ökar när ritualen fördjupas. Hög dread låser upp nya platser och händelser.</li>
              <li><strong>Hemlighet</strong> sjunker när ni blir för uppenbara. Når den noll är det slut.</li>
              <li><strong>Kaos</strong> byggs upp naturligt och utlöser kaotiska händelser när det når 100.</li>
              <li><strong>Ritual</strong> låses upp via Skriften. Nå 100 för att fullborda ceremonin.</li>
              <li>Klicka på <em>platser på kartan</em> för att besöka dem. Varje besök lämnar spår.</li>
              <li>Händelser inträffar automatiskt. De flesta är dåliga. En del är värre.</li>
            </ul>
          </section>
          <section class="help-section">
            <div class="help-section-title">Om spelet</div>
            <p class="help-flavor">
              Djupet är ett idle-spel om en kult i Malmö, Sverige.<br>
              Inspirerat av Anders Fager, Kult och Call of Cthulhu.<br>
              Den Sovande är inte vaken ännu. Jobba på det.
            </p>
          </section>
          <section class="help-section">
            <div class="help-section-title">Ändringslogg</div>
            ${CHANGELOG.map(e => `
              <div class="help-changelog-entry">
                <div class="help-changelog-version">v${e.version} <span class="help-changelog-date">${e.date}</span></div>
                <ul class="help-list">${e.changes.map(c => `<li>${c}</li>`).join('')}</ul>
              </div>
            `).join('')}
          </section>
        </div>
      </div>
    </div>

    <div id="characters-modal" class="help-modal">
      <div class="help-modal-inner" id="characters-modal-inner">
        <div class="help-modal-header">
          <span class="help-modal-title">Sällskapet</span>
          <button id="characters-close" class="help-close">✕</button>
        </div>
        <div class="help-modal-body">
          <p class="help-flavor" style="margin-bottom:14px;">De som suttit vid bordet. Vissa längre än andra.</p>
          <div id="characters-grid" class="characters-grid"></div>
        </div>
      </div>
    </div>

    <div class="panel" id="panel-left">
      <div class="panel-title">Resurser</div>

      <div class="resource">
        <div class="resource-label">Troende</div>
        <div class="resource-value" id="r-troende">1</div>
      </div>

      <div class="resource">
        <div class="resource-label">Andakt <span id="r-andakt-rate" style="color:var(--dim);font-size:10px;"></span></div>
        <div class="resource-value" id="r-andakt">0</div>
      </div>

      <div class="resource">
        <div class="resource-label">Inflytande</div>
        <div class="resource-value" id="r-inflytande">0</div>
      </div>

      <div class="resource">
        <div class="resource-label">Dread <span id="r-dread-rate" style="color:var(--dread);font-size:10px;"></span></div>
        <div class="resource-value" style="color:var(--dread);" id="r-dread">0</div>
      </div>

      <div class="resource bar-hemlighet" style="margin-top:16px;">
        <div class="resource-label">Hemlighet</div>
        <div class="resource-value" id="r-hemlighet">100</div>
        <div class="resource-bar"><div class="resource-bar-fill" id="bar-hemlighet" style="width:100%"></div></div>
      </div>

      <div class="resource bar-kaos">
        <div class="resource-label">Kaos</div>
        <div class="resource-value" id="r-kaos">0</div>
        <div class="resource-bar"><div class="resource-bar-fill" id="bar-kaos" style="width:0%"></div></div>
      </div>

      <div class="resource bar-ritual" id="ritual-section" style="display:none;">
        <div class="resource-label">Ritual</div>
        <div class="resource-value" style="color:var(--ritual);" id="r-ritual">0</div>
        <div class="resource-bar"><div class="resource-bar-fill" id="bar-ritual" style="width:0%"></div></div>
      </div>

      <div style="margin-top:20px;">
        <div class="panel-title">Åtgärder</div>
        <div id="upgrades"></div>
      </div>
    </div>

    <div id="map-container">
      <div id="map"></div>
      <div id="log"></div>
    </div>

    <div class="panel" id="panel-right">
      <div class="panel-title">Ritualens kärnplatser</div>
      <div id="areas" style="font-size:11px;color:var(--dim);line-height:2;">
        <div id="area-rosengard">○ Rosengård</div>
        <div id="area-varnhem">○ Värnhemstorget</div>
        <div id="area-torso">○ Turning Torso</div>
      </div>
      <div style="margin-top:16px;">
        <div class="panel-title">Övriga platser</div>
        <div style="font-size:11px;color:var(--dim);line-height:2;">
          <div>Øresundsbron</div>
          <div>Ribersborgsstranden</div>
          <div>Davidshallsgatan</div>
          <div>Stortorget</div>
        </div>
      </div>
      <div style="margin-top:20px;" id="investigator-section">
        <div class="panel-title">Utredarna</div>
        <div id="investigator-status" style="font-size:11px;color:var(--safe);">Inaktiva</div>
      </div>
    </div>

    <div id="overlay">
      <div class="overlay-scrim">
        <div class="overlay-title" id="overlay-title"></div>
        <div class="overlay-body" id="overlay-body"></div>
        <button class="overlay-btn" id="overlay-btn">Börja om</button>
      </div>
    </div>

    <div id="danger-vignette"></div>

    <div id="event-modal" class="help-modal">
      <div class="help-modal-inner" id="event-modal-inner">
        <div class="help-modal-body">
          <div class="event-modal-text" id="event-modal-text"></div>
          <div class="event-modal-choices" id="event-modal-choices"></div>
        </div>
      </div>
    </div>
  `;

  document.getElementById('overlay-btn')!.addEventListener('click', onRestart);
  document.getElementById('start-btn')!.addEventListener('click', onStart);
  document.getElementById('abandon-btn')!.addEventListener('click', () => {
    if (window.confirm('Överge kulten? Cirkeln bryts och ni går skilda vägar.')) onAbandon();
  });
  document.getElementById('pause-btn')!.addEventListener('click', onTogglePause);

  document.getElementById('event-modal-choices')!.addEventListener('click', e => {
    const btn = (e.target as Element).closest('[data-choice]') as HTMLElement | null;
    if (btn?.dataset['choice'] !== undefined) onEventChoice(Number(btn.dataset['choice']));
  });

  const helpModal = document.getElementById('help-modal')!;
  const helpInner = document.getElementById('help-modal-inner')!;
  document.getElementById('help-btn')!.addEventListener('click', () => helpModal.classList.toggle('visible'));
  document.getElementById('help-close')!.addEventListener('click', () => helpModal.classList.remove('visible'));
  helpModal.addEventListener('click', (e) => { if (!helpInner.contains(e.target as Node)) helpModal.classList.remove('visible'); });
  document.addEventListener('keydown', (e) => { if (e.key === '?' || (e.key === 'Escape' && helpModal.classList.contains('visible'))) helpModal.classList.toggle('visible'); });

  const charactersModal = document.getElementById('characters-modal')!;
  const charactersInner = document.getElementById('characters-modal-inner')!;
  document.getElementById('characters-btn')!.addEventListener('click', () => {
    renderCharacterGrid(latestState);
    charactersModal.classList.toggle('visible');
  });
  document.getElementById('characters-close')!.addEventListener('click', () => charactersModal.classList.remove('visible'));
  charactersModal.addEventListener('click', (e) => { if (!charactersInner.contains(e.target as Node)) charactersModal.classList.remove('visible'); });

  // Split the map container: map on top, log below
  const mapContainer = document.getElementById('map-container')!;
  mapContainer.style.cssText = 'display:flex;flex-direction:column;overflow:hidden;height:100%;';
  const mapEl = document.getElementById('map')!;
  mapEl.style.cssText = 'flex:0 0 45%;min-height:0;max-height:45%;';
  const logEl = document.getElementById('log')!;
  logEl.style.cssText = 'flex:1 1 0;overflow-y:auto;border-top:1px solid var(--border);min-height:0;';

  // Upgrade click delegation
  document.getElementById('upgrades')!.addEventListener('click', e => {
    const btn = (e.target as Element).closest('[data-upgrade]') as HTMLElement | null;
    if (btn?.dataset['upgrade']) onUpgrade(btn.dataset['upgrade']);
  });
}

let latestState: State | null = null;

export function renderState(state: State): void {
  latestState = state;
  setText('r-troende', String(state.troende));
  setText('r-andakt', fmt(state.andakt));
  setText('r-andakt-rate', `+${(state.andaktRate * state.troende).toFixed(1)}/s`);
  setText('r-inflytande', fmt(state.inflytande));
  setText('r-dread', fmt(state.dread));
  setText('r-dread-rate', state.dreadRate > 0 ? `+${state.dreadRate.toFixed(1)}/s` : '');
  setText('r-hemlighet', Math.floor(state.hemlighet).toString());
  setText('r-kaos', Math.floor(state.kaos).toString());
  setBar('bar-hemlighet', state.hemlighet);
  setBar('bar-kaos', state.kaos);

  // Ambient visual reactivity — the deeper the dread, the more the world tilts; low hemlighet bleeds red at the edges.
  const root = document.documentElement.style;
  root.setProperty('--dread-intensity', Math.min(1, state.dread / 150).toFixed(3));
  root.setProperty('--hemlighet-danger', (state.hemlighet < 40 ? (1 - state.hemlighet / 40) * 0.35 : 0).toFixed(3));

  const ritualSection = document.getElementById('ritual-section')!;
  if (state.ritualUnlocked) {
    ritualSection.style.display = '';
    setText('r-ritual', Math.floor(state.ritual).toString());
    setBar('bar-ritual', state.ritual);
  }

  // Phase tag
  const phaseEl = document.getElementById('phase-tag')!;
  if (state.ritual >= 50) {
    phaseEl.textContent = 'Sent skede'; phaseEl.className = 'phase-tag late';
  } else if (state.dread >= 30) {
    phaseEl.textContent = 'Mitt skede'; phaseEl.className = 'phase-tag mid';
  } else {
    phaseEl.textContent = 'Tidigt skede'; phaseEl.className = 'phase-tag early';
  }

  // Areas
  setArea('area-rosengard', state.areas.rosengard, 'Rosengård');
  setArea('area-varnhem', state.areas.varnhem, 'Värnhemstorget');
  setArea('area-torso', state.areas.torso, 'Turning Torso');

  // Investigator status
  const inv = document.getElementById('investigator-status')!;
  if (state.hemlighet < 20) {
    inv.textContent = 'Nära — väldigt nära'; inv.style.color = 'var(--danger)';
  } else if (state.hemlighet < 50) {
    inv.textContent = 'Misstänksamma'; inv.style.color = 'var(--kaos)';
  } else {
    inv.textContent = 'Inaktiva'; inv.style.color = 'var(--safe)';
  }

  // Upgrades
  renderUpgrades(state);

  // Log
  renderLog(state);

  // Intro screen
  document.getElementById('intro-screen')!.classList.toggle('visible', state.phase === 'intro');

  // Overlay
  const overlay = document.getElementById('overlay')!;
  if (state.phase === 'won' || state.phase === 'lost' || state.phase === 'abandoned') {
    showOverlay(state);
  } else {
    overlay.classList.remove('visible');
  }

  // Event choice modal
  renderEventModal(state);
}

let lastPendingEventId: string | null = null;

function renderEventModal(state: State): void {
  const modal = document.getElementById('event-modal')!;
  const pending = state.pendingEvent;
  const id = pending?.event.id ?? null;
  if (id === lastPendingEventId) return;
  lastPendingEventId = id;

  if (!pending) {
    modal.classList.remove('visible');
    return;
  }

  document.getElementById('event-modal-text')!.textContent = pending.event.text;
  const choices = document.getElementById('event-modal-choices')!;
  choices.innerHTML = (pending.event.choices ?? []).map((c, i) =>
    `<button class="upgrade-btn" data-choice="${i}"><span class="up-name">${c.text}</span></button>`
  ).join('');
  modal.classList.add('visible');
}

let lastUpgradeKey = '';

function renderUpgrades(state: State): void {
  const container = document.getElementById('upgrades')!;
  const available = UPGRADES.filter(u => u.available(state) && (u.once ? !state.upgrades.has(u.id) : true));

  // Only rebuild DOM when availability or affordability changes
  const key = available.map(u => {
    const cost = u.cost(state);
    const canAfford = (!cost.andakt || state.andakt >= cost.andakt) && (!cost.dread || state.dread >= cost.dread) && (!cost.inflytande || state.inflytande >= cost.inflytande);
    return `${u.id}:${canAfford}:${cost.andakt ?? ''}`;
  }).join('|');

  if (key === lastUpgradeKey) return;
  lastUpgradeKey = key;

  if (!available.length) {
    container.innerHTML = '<div style="font-size:11px;color:var(--dim);">Inga tillgängliga åtgärder.</div>';
    return;
  }

  container.innerHTML = available.map(u => {
    const cost = u.cost(state);
    const canAfford = (!cost.andakt || state.andakt >= cost.andakt) && (!cost.dread || state.dread >= cost.dread) && (!cost.inflytande || state.inflytande >= cost.inflytande);
    const costs = [
      cost.andakt ? `${cost.andakt} andakt` : '',
      cost.dread  ? `${cost.dread} dread`   : '',
      cost.inflytande ? `${cost.inflytande} inflytande` : '',
    ].filter(Boolean).join(', ');
    return `<button class="upgrade-btn" data-upgrade="${u.id}" ${canAfford ? '' : 'disabled'}>
      <span class="up-name">${u.name}</span>
      <span class="up-desc">${u.desc}</span>
      <span class="up-cost">Kostnad: ${costs || '—'}</span>
    </button>`;
  }).join('');
}

let lastLogSeq = -1;
let typewriterTimer: number | undefined;

// Occult stand-ins for the typewriter effect on the newest log line — a
// deeper ritual means a shakier grip on plain Swedish. Never applied
// retroactively to older entries, so history stays legible.
const GLITCH_CHARS = ['☿', '☠', 'ψ', 'Ψ', '⛧', '҂'];

function glitchChar(dreadIntensity: number): string | null {
  const chance = Math.min(0.18, dreadIntensity * 0.22);
  if (Math.random() < chance) return GLITCH_CHARS[Math.floor(Math.random() * GLITCH_CHARS.length)]!;
  return null;
}

function renderLog(state: State): void {
  const log = document.getElementById('log')!;
  if (state.logSeq === lastLogSeq) return;
  const isFirstRender = lastLogSeq === -1;
  lastLogSeq = state.logSeq;

  // state.log is newest-first (unshift). The newest line gets its own
  // element so it can type itself out; everything else renders plainly.
  const [newest, ...rest] = state.log;
  log.innerHTML = rest.map(e => `<div class="log-entry ${e.kind}">${e.text}</div>`).join('')
    + (newest ? `<div class="log-entry ${newest.kind}" id="log-newest"></div>` : '');
  log.scrollTop = 0;

  if (typewriterTimer !== undefined) { window.clearInterval(typewriterTimer); typewriterTimer = undefined; }

  if (newest) {
    const el = document.getElementById('log-newest')!;
    if (isFirstRender || state.phase !== 'playing') {
      // Don't animate the save you just loaded, or the ending screen's line.
      el.textContent = newest.text;
    } else {
      const dreadIntensity = Math.min(1, state.dread / 150);
      const text = newest.text;
      const stepMs = text.length > 120 ? 8 : 18;
      let i = 0;
      typewriterTimer = window.setInterval(() => {
        i += 1;
        el.textContent = text.slice(0, i).split('').map(ch => (ch === ' ' ? ch : glitchChar(dreadIntensity) ?? ch)).join('');
        if (i >= text.length) {
          window.clearInterval(typewriterTimer);
          typewriterTimer = undefined;
        }
      }, stepMs);
    }
  }

  if (state.log[0]?.kind === 'kaos') {
    const app = document.getElementById('app')!;
    app.classList.remove('kaos-glitch');
    void app.offsetWidth; // restart animation
    app.classList.add('kaos-glitch');
  }
}

function renderCharacterGrid(state: State | null): void {
  const grid = document.getElementById('characters-grid')!;
  const met = new Set(state?.usedEventIds ?? []);
  grid.innerHTML = CHARACTERS.map(c => {
    const known = !c.unlockEventId || met.has(c.unlockEventId);
    if (!known) {
      return `
        <div class="character-card locked">
          <div class="character-portrait locked-portrait">?</div>
          <div class="character-name">???</div>
          <div class="character-role">Inte mött än.</div>
        </div>`;
    }
    return `
      <div class="character-card" data-char="${c.id}">
        <div class="character-portrait"><img alt="${c.name}" loading="lazy"></div>
        <div class="character-name">${c.name}</div>
        <div class="character-role">${c.role}</div>
        <div class="character-bio">${c.bio}</div>
      </div>`;
  }).join('');

  // Portraits are optional — art may not exist yet for every character.
  // Fall back to an initial-letter placeholder rather than a broken image.
  for (const c of CHARACTERS) {
    const card = grid.querySelector(`[data-char="${c.id}"]`);
    const img = card?.querySelector('img');
    if (!img) continue;
    img.addEventListener('error', () => {
      const fallback = document.createElement('div');
      fallback.className = 'character-portrait-fallback';
      fallback.textContent = c.name.charAt(0);
      img.replaceWith(fallback);
    }, { once: true });
    img.src = c.img;
  }
}

function showOverlay(state: State): void {
  const ov = document.getElementById('overlay')!;
  ov.classList.add('visible');
  const title = document.getElementById('overlay-title')!;
  const body = document.getElementById('overlay-body')!;
  if (state.phase === 'won') {
    ov.style.backgroundImage = "url('/img/win.webp')";
    title.textContent = 'FULLBORDAT';
    title.className = 'overlay-title won';
    body.textContent = 'Det är gjort. Turning Torso pekar rakt ned. Havet är stilla — alltför stilla. Malmö fortsätter som vanligt. Det är det värsta.';
  } else if (state.phase === 'abandoned') {
    ov.style.backgroundImage = "url('/img/fail.webp')";
    title.textContent = 'ÖVERGIVET';
    title.className = 'overlay-title lost';
    body.textContent = 'Ni lämnar källaren. Cirkeln bryts. Den Sovande sover vidare, ovetande, ofärdig.';
  } else {
    ov.style.backgroundImage = "url('/img/fail.webp')";
    title.textContent = 'AVSLÖJAD';
    title.className = 'overlay-title lost';
    body.textContent = state.lostReason ?? 'Utredarna kom.';
  }
}

export function setPausedUI(paused: boolean): void {
  const btn = document.getElementById('pause-btn')!;
  btn.textContent = paused ? 'Fortsätt' : 'Pausa';
  document.getElementById('app')!.classList.toggle('paused', paused);
  setText('pause-tag', paused ? '⏸ Pausad' : '');
}

function setText(id: string, val: string): void {
  const el = document.getElementById(id);
  if (el) el.textContent = val;
}

function setBar(id: string, pct: number): void {
  const el = document.getElementById(id) as HTMLElement | null;
  if (el) el.style.width = `${Math.min(100, Math.max(0, pct))}%`;
}

function setArea(id: string, active: boolean, label: string): void {
  const el = document.getElementById(id);
  if (el) {
    el.textContent = `${active ? '●' : '○'} ${label}`;
    el.style.color = active ? 'var(--dread)' : 'var(--dim)';
  }
}

function fmt(n: number): string {
  return Math.floor(n).toLocaleString('sv-SE');
}
