import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import test from 'node:test';
import vm from 'node:vm';

const root = path.resolve(import.meta.dirname, '..');

async function read(relativePath) {
  return fs.readFile(path.join(root, relativePath), 'utf8');
}

test('start screen exposes cookie status, optional local progress and deliberate entry', async () => {
  const html = await read('public/index.html');
  const loader = await read('public/js/bootstrap-loader.js');
  const storage = await read('public/js/privacy-storage.js');

  assert.match(html, /Diese Anwendung setzt derzeit keine Cookies/);
  assert.match(html, /class="cookie-toggle"[^>]*disabled/);
  assert.match(html, /class="progress-storage-toggle"/);
  assert.match(html, /id="storage-cleanup-status"/);
  assert.match(html, /id="bootstrap-enter"[^>]*hidden/);
  assert.match(loader, /Entferne alte Streckendaten aus dem Speicher \(\$\{removed\} \/ \$\{total\}\)/);
  assert.match(loader, /showReadyState\(\)/);
  assert.doesNotMatch(loader, /getElementById\('bootstrap-screen'\)\.hidden = true/);
  assert.match(loader, /event\.target\.closest\('button, input, label, a, dialog'\)/);
  assert.match(storage, /MAX_PROGRESS_AGE_MS = 24 \* 60 \* 60 \* 1000/);
  assert.match(storage, /cookiesEnabled: \(\) => false/);
});

test('information button opens scrollable overlay pages for privacy, imprint and technology', async () => {
  const html = await read('public/index.html');
  const css = await read('public/css/app.css');
  const ui = await read('public/js/legal-ui.js');

  assert.match(html, /id="info-button"/);
  assert.match(html, /<dialog id="info-dialog"/);
  assert.match(html, /data-info-target="privacy"/);
  assert.match(html, /data-info-target="imprint"/);
  assert.match(html, /data-info-target="technology"/);
  assert.match(html, /wegwichtel\.privacy\.v1/);
  assert.match(html, /wegwichtel\.route-progress\.v1/);
  assert.match(html, /jQuery 4\.0\.0/);
  assert.match(css, /\.info-dialog-content[\s\S]*overflow-y:\s*auto/);
  assert.match(css, /\.info-dialog-content[\s\S]*touch-action:\s*pan-y/);
  assert.match(ui, /pointerup/);
  assert.match(ui, /keydown/);
  assert.doesNotMatch(ui, /addEventListener\(['"]click['"]/);
});

test('application persists current route, viewed stations and played audio only through privacy storage', async () => {
  const app = await read('public/js/app.js');

  assert.match(app, /PrivacyStorage\.saveRouteState/);
  assert.match(app, /PrivacyStorage\.activateRoute/);
  assert.match(app, /state\.viewed\.add\(state\.activePoiId\)/);
  assert.match(app, /\$\('#poi-audio'\)\.on\('play'/);
  assert.match(app, /state\.playedAudio\.add\(state\.activePoiId\)/);
  assert.match(app, /globalThis\.addEventListener\('pagehide', persistRouteProgress\)/);
  assert.match(app, /await initializeLocation\(context\)/);
});


class FakeElement extends EventTarget {
  constructor(id = '') {
    super();
    this.id = id;
    this.hidden = false;
    this.checked = false;
    this.disabled = false;
    this.open = false;
    this.textContent = '';
    this.scrollTop = 0;
    this.dataset = {};
    this.attributes = new Map();
    this.focused = false;
  }

  focus() {
    this.focused = true;
  }

  setAttribute(name, value) {
    this.attributes.set(name, String(value));
    if (name === 'open') this.open = true;
  }

  removeAttribute(name) {
    this.attributes.delete(name);
    if (name === 'open') this.open = false;
  }

  showModal() {
    this.open = true;
  }

  close() {
    this.open = false;
  }
}

function pointerEvent() {
  const event = new Event('pointerup', { bubbles: true, cancelable: true });
  Object.defineProperties(event, {
    isPrimary: { value: true },
    pointerType: { value: 'touch' },
    button: { value: 0 }
  });
  return event;
}

test('legal overlay handlers synchronize switches without advancing the start screen', async () => {
  const sourcePath = path.join(root, 'public/js/legal-ui.js');
  const source = await fs.readFile(sourcePath, 'utf8');
  const elements = new Map([
    ['info-dialog', new FakeElement('info-dialog')],
    ['info-dialog-title', new FakeElement('info-dialog-title')],
    ['info-menu', new FakeElement('info-menu')],
    ['info-back', new FakeElement('info-back')],
    ['info-close', new FakeElement('info-close')],
    ['info-button', new FakeElement('info-button')]
  ]);
  const content = new FakeElement();
  const privacyPage = new FakeElement();
  const imprintPage = new FakeElement();
  const technologyPage = new FakeElement();
  const pages = { privacy: privacyPage, imprint: imprintPage, technology: technologyPage };
  const targetButtons = Object.keys(pages).map(name => {
    const button = new FakeElement();
    button.dataset.infoTarget = name;
    return button;
  });
  const progressToggles = [new FakeElement(), new FakeElement()];
  const cookieToggles = [new FakeElement()];
  let enabled = false;
  const changes = [];

  class TestCustomEvent extends Event {
    constructor(type, options = {}) {
      super(type);
      this.detail = options.detail;
    }
  }

  const document = new EventTarget();
  document.getElementById = id => elements.get(id);
  document.querySelectorAll = selector => {
    if (selector === '.progress-storage-toggle') return progressToggles;
    if (selector === '.cookie-toggle') return cookieToggles;
    if (selector === '[data-info-page]') return Object.values(pages);
    if (selector === '[data-info-target]') return targetButtons;
    return [];
  };
  document.querySelector = selector => {
    if (selector === '.info-dialog-content') return content;
    const match = selector.match(/^\[data-info-page="(.+)"\]$/);
    return match ? pages[match[1]] : null;
  };
  document.addEventListener('wegwichtel:progress-storage-changed', event => changes.push(event.detail.enabled));

  const context = {
    Wegwichtel: {
      PrivacyStorage: {
        isProgressStorageEnabled: () => enabled,
        setProgressStorageEnabled: value => {
          enabled = value;
          return enabled;
        }
      }
    },
    document,
    Event,
    EventTarget,
    CustomEvent: TestCustomEvent
  };
  context.globalThis = context;
  vm.runInNewContext(source, context, { filename: sourcePath });

  elements.get('info-button').dispatchEvent(pointerEvent());
  assert.equal(elements.get('info-dialog').open, true);
  assert.equal(elements.get('info-menu').hidden, false);

  targetButtons[0].dispatchEvent(pointerEvent());
  assert.equal(privacyPage.hidden, false);
  assert.equal(elements.get('info-back').hidden, false);
  assert.equal(elements.get('info-dialog-title').textContent, 'Datenschutzerklärung');

  progressToggles[0].checked = true;
  progressToggles[0].dispatchEvent(new Event('change'));
  assert.deepEqual(changes, [true]);
  assert.equal(progressToggles[1].checked, true);
  assert.equal(cookieToggles[0].checked, false);

  elements.get('info-back').dispatchEvent(pointerEvent());
  assert.equal(elements.get('info-menu').hidden, false);
  elements.get('info-close').dispatchEvent(pointerEvent());
  assert.equal(elements.get('info-dialog').open, false);
});
