// @vitest-environment node
/**
 * Die Stufenlogik der Anzeigegröße.
 *
 * Reine Rechnerei, ohne Fenster: welche Stufe auf welche folgt, wie
 * unbrauchbare Werte bereinigt werden, wo die Grenzen liegen. Ob der Zoom im
 * echten Fenster ankommt, misst `e2e/anzeigegroesse.spec.ts`.
 */

import { describe, expect, it } from 'vitest';

import {
  ANZEIGEGROESSEN,
  ANZEIGEGROESSE_STANDARD,
  anzeigegroesseBereinigen,
  anzeigegroesseText,
  groesser,
  istGroesstmoeglich,
  istKleinstmoeglich,
  kleiner,
  zoomfaktor,
} from '../src/shared/ansicht';

describe('Stufen', () => {
  it('enthält die Vorgabe und reicht bis 400 Prozent', () => {
    /* WCAG 1.4.4 verlangt, dass sich Text auf 200 Prozent vergrößern lässt,
       ohne dass Inhalt oder Funktion verloren gehen. Weniger anzubieten wäre
       ein Verstoß, den niemand sieht.

       1.4.10 (Reflow) misst bei 400 Prozent auf 1280 Pixeln Fensterbreite,
       also bei 320 CSS-Pixeln. Ohne eine 400er-Stufe wäre dieser Prüffall
       mit Bordmitteln nicht herstellbar. */
    expect(ANZEIGEGROESSEN).toContain(ANZEIGEGROESSE_STANDARD);
    expect(Math.max(...ANZEIGEGROESSEN)).toBeGreaterThanOrEqual(400);
  });

  it('bietet zwischen 200 und 400 Prozent Zwischenstufen an', () => {
    /* Ein Sprung von 200 auf 400 wäre eine Verdopplung in einem Klick: Wer
       225 Prozent braucht, säße vor einer Skala, die ihn übergeht. */
    const oben = ANZEIGEGROESSEN.filter((stufe) => stufe > 200 && stufe < 400);

    expect(oben.length).toBeGreaterThanOrEqual(2);
  });

  it('steigt lückenlos an', () => {
    const sortiert = [...ANZEIGEGROESSEN].sort((a, b) => a - b);

    expect([...ANZEIGEGROESSEN]).toEqual(sortiert);
    expect(new Set(ANZEIGEGROESSEN).size).toBe(ANZEIGEGROESSEN.length);
  });
});

describe('anzeigegroesseBereinigen', () => {
  it('lässt gültige Stufen stehen', () => {
    for (const stufe of ANZEIGEGROESSEN) {
      expect(anzeigegroesseBereinigen(stufe)).toBe(stufe);
    }
  });

  it('nimmt für Unbrauchbares die Vorgabe', () => {
    for (const wert of [undefined, null, 'gross', Number.NaN, {}, [], true]) {
      expect(anzeigegroesseBereinigen(wert)).toBe(ANZEIGEGROESSE_STANDARD);
    }
  });

  it('begrenzt nach oben und unten', () => {
    expect(anzeigegroesseBereinigen(5000)).toBe(400);
    expect(anzeigegroesseBereinigen(-10)).toBe(80);
  });

  it('rundet zur nächstkleineren Stufe ab', () => {
    /* Nicht zur nächstgelegenen: Wer einen Zwischenwert gespeichert hat,
       soll nicht plötzlich größer sehen als vorher. */
    expect(anzeigegroesseBereinigen(149)).toBe(125);
    expect(anzeigegroesseBereinigen(101)).toBe(100);
  });
});

describe('Stufenwechsel', () => {
  it('geht Schritt für Schritt hoch und wieder herunter', () => {
    let wert: number = ANZEIGEGROESSE_STANDARD;
    const hoch: number[] = [];
    for (let i = 0; i < ANZEIGEGROESSEN.length + 3; i++) {
      wert = groesser(wert);
      hoch.push(wert);
    }

    expect(hoch.at(-1)).toBe(400);
    expect(new Set(hoch).size).toBeLessThan(hoch.length); // bleibt oben stehen

    let runter = 400;
    for (let i = 0; i < ANZEIGEGROESSEN.length + 3; i++) {
      runter = kleiner(runter);
    }
    expect(runter).toBe(80);
  });

  it('bleibt an den Enden stehen, statt umzuspringen', () => {
    expect(groesser(400)).toBe(400);
    expect(kleiner(80)).toBe(80);
    expect(istGroesstmoeglich(400)).toBe(true);
    expect(istKleinstmoeglich(80)).toBe(true);
    expect(istGroesstmoeglich(200)).toBe(false);
    expect(istGroesstmoeglich(100)).toBe(false);
  });

  it('kommt auch von einem ungültigen Ausgangswert weiter', () => {
    expect(groesser(Number.NaN)).toBe(groesser(ANZEIGEGROESSE_STANDARD));
  });
});

describe('Darstellung', () => {
  it('rechnet Prozent in Chromiums Zoomfaktor um', () => {
    expect(zoomfaktor(100)).toBe(1);
    expect(zoomfaktor(200)).toBe(2);
    expect(zoomfaktor(400)).toBe(4);
    expect(zoomfaktor(80)).toBeCloseTo(0.8, 5);
  });

  it('schreibt Prozent aus, statt das Zeichen zu setzen', () => {
    /* „125 %" liest ein Screenreader je nach Stimme als „125" – die
       Einheit ginge verloren. */
    expect(anzeigegroesseText(125)).toBe('125 Prozent');
    expect(anzeigegroesseText(9999)).toBe('400 Prozent');
  });
});
