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, '..');

class ClassList {
  constructor() {
    this.values = new Set();
  }

  add(...names) {
    names.forEach(name => this.values.add(name));
  }

  remove(...names) {
    names.forEach(name => this.values.delete(name));
  }

  contains(name) {
    return this.values.has(name);
  }
}

class FakeElement extends EventTarget {
  constructor(id = '') {
    super();
    this.id = id;
    this.hidden = false;
    this.value = 0;
    this.textContent = '';
    this.src = '';
    this.async = true;
    this.classList = new ClassList();
    this.attributes = new Map();
    this.focused = false;
  }

  focus() {
    this.focused = true;
  }

  setAttribute(name, value) {
    this.attributes.set(name, String(value));
  }

  closest() {
    return null;
  }
}

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

test('bootstrap remains visible after loading and opens the app only after deliberate activation', async () => {
  const sourcePath = path.join(root, 'public/js/bootstrap-loader.js');
  const source = await fs.readFile(sourcePath, 'utf8');
  const ids = [
    'bootstrap-screen',
    'bootstrap-progress',
    'bootstrap-error',
    'bootstrap-retry',
    'bootstrap-enter',
    'bootstrap-ready',
    'storage-cleanup-status',
    'app-shell'
  ];
  const elements = new Map(ids.map(id => [id, new FakeElement(id)]));
  const steps = new Map([
    'jquery',
    'jquery-ui',
    'modules',
    'server',
    'routes',
    'location'
  ].map(name => [name, new FakeElement(name)]));
  const navigations = [];

  function jquery() {
    return {
      button() {
        return this;
      }
    };
  }

  const context = {
    Wegwichtel: {
      PrivacyStorage: {
        async cleanupExpired(callback) {
          callback(0, 1);
          callback(1, 1);
          return { removed: 1, total: 1 };
        }
      },
      App: {
        async initialize({ markStep }) {
          markStep('server', 'done');
          markStep('routes', 'done');
          markStep('location', 'done');
        },
        navigate(...args) {
          navigations.push(args);
        }
      }
    },
    document: {
      getElementById: id => elements.get(id),
      querySelector(selector) {
        const match = selector.match(/^\[data-step="(.+)"\]$/);
        return match ? steps.get(match[1]) : null;
      },
      querySelectorAll(selector) {
        if (selector === '#bootstrap-steps .done') {
          return [...steps.values()].filter(step => step.classList.contains('done'));
        }
        return [];
      },
      createElement: () => new FakeElement('script'),
      head: {
        appendChild(script) {
          if (script.src.includes('/jquery-4.0.0.min.js')) {
            jquery.fn = { jquery: '4.0.0' };
            context.jQuery = jquery;
          }
          if (script.src.includes('/jquery-ui-1.14.2.min.js')) {
            jquery.ui = { version: '1.14.2' };
          }
          queueMicrotask(() => script.onload());
        }
      }
    },
    Event,
    EventTarget,
    Promise,
    console,
    encodeURIComponent,
    queueMicrotask,
    location: { reload() {} }
  };
  context.globalThis = context;
  vm.runInNewContext(source, context, { filename: sourcePath });

  await new Promise(resolve => setTimeout(resolve, 20));
  assert.equal(elements.get('bootstrap-screen').hidden, false);
  assert.equal(elements.get('bootstrap-screen').attributes.get('aria-busy'), 'false');
  assert.equal(elements.get('bootstrap-enter').hidden, false);
  assert.equal(elements.get('storage-cleanup-status').textContent, 'Entferne alte Streckendaten aus dem Speicher (1 / 1)');
  assert.equal(navigations.length, 0);

  elements.get('bootstrap-screen').dispatchEvent(pointerEvent());
  assert.equal(elements.get('bootstrap-screen').hidden, true);
  assert.equal(elements.get('app-shell').hidden, false);
  assert.equal(navigations[0][0], '#routes-page');
  assert.equal(navigations[0][1].replace, true);
});
