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

import {
  farbschemaFuer,
  istThemeAuswahl,
  startfarbeFuer,
  themeAufloesen,
  umschaltAnsage,
  type SystemAnzeigeSignale,
} from '../src/shared/theme';

function signale(teil: Partial<SystemAnzeigeSignale> = {}): SystemAnzeigeSignale {
  return {
    bevorzugtDunkel: false,
    erzwungeneFarben: false,
    bevorzugtMehrKontrast: false,
    ...teil,
  };
}

describe('themeAufloesen', () => {
  it('folgt bei "system" der hellen Systemvorgabe', () => {
    expect(themeAufloesen('system', signale())).toBe('hell');
  });

  it('folgt bei "system" der dunklen Systemvorgabe', () => {
    expect(themeAufloesen('system', signale({ bevorzugtDunkel: true }))).toBe('dunkel');
  });

  it('wählt bei "system" und prefers-contrast: more den hohen Kontrast', () => {
    expect(themeAufloesen('system', signale({ bevorzugtMehrKontrast: true }))).toBe('hochkontrast');
  });

  it('respektiert die manuelle Auswahl gegen die Systemvorgabe', () => {
    expect(themeAufloesen('hell', signale({ bevorzugtDunkel: true }))).toBe('hell');
    expect(themeAufloesen('dunkel', signale())).toBe('dunkel');
  });

  it('lässt erzwungene Farben jede Auswahl überstimmen', () => {
    for (const auswahl of ['system', 'hell', 'dunkel', 'hochkontrast'] as const) {
      expect(themeAufloesen(auswahl, signale({ erzwungeneFarben: true }))).toBe('hochkontrast');
    }
  });
});

describe('istThemeAuswahl', () => {
  it('erkennt gültige Werte', () => {
    expect(istThemeAuswahl('hochkontrast')).toBe(true);
  });

  it('weist unbekannte oder falsch typisierte Werte ab', () => {
    expect(istThemeAuswahl('neon')).toBe(false);
    expect(istThemeAuswahl(null)).toBe(false);
    expect(istThemeAuswahl(42)).toBe(false);
    expect(istThemeAuswahl(undefined)).toBe(false);
  });
});

describe('farbschemaFuer', () => {
  it('stimmt mit den color-scheme-Angaben in tokens.css überein', () => {
    expect(farbschemaFuer('hell')).toBe('light');
    expect(farbschemaFuer('dunkel')).toBe('dark');
    // Hoher Kontrast ist weiße Schrift auf Schwarz – also ebenfalls dark.
    expect(farbschemaFuer('hochkontrast')).toBe('dark');
  });

  /*
    Beim Windows-Kontrastdesign sagt `hochkontrast` nur, dass die Farben von
    aussen kommen. Es gibt auch helle Kontrastdesigns; bis 0.24.1 stellte die
    Anwendung dort Scrollbalken und Formularelemente unbedingt auf dunkel.
    `startfarbeFuer` macht es seit jeher richtig – hier fehlte die
    Entsprechung.
  */
  it('überlässt bei erzwungenen Farben dem System, ob hell oder dunkel', () => {
    expect(farbschemaFuer('hochkontrast', signale({ erzwungeneFarben: true }))).toBe('light');
    expect(
      farbschemaFuer('hochkontrast', signale({ erzwungeneFarben: true, bevorzugtDunkel: true })),
    ).toBe('dark');
  });

  it('bleibt dieselbe Aussage wie startfarbeFuer', () => {
    /* Zwei Funktionen, eine Frage – sie dürfen nicht auseinanderlaufen. */
    for (const dunkel of [false, true]) {
      const s = signale({ erzwungeneFarben: true, bevorzugtDunkel: dunkel });
      const hellSchema = farbschemaFuer('hochkontrast', s) === 'light';
      expect(startfarbeFuer('hochkontrast', s)).toBe(hellSchema ? '#ffffff' : '#000000');
    }
  });
});

describe('startfarbeFuer', () => {
  /*
    Die Farbe, die das Fenster zeigt, bevor der Renderer das erste Bild
    liefert. Bis 0.21.0 fragte `fenster.ts` dafür nur `nativeTheme` – wer
    „Dunkel“ eingestellt hatte, während Windows hell läuft, sah bei jedem
    Start kurz eine helle Fläche. Seither entscheidet die gespeicherte Wahl.
  */
  it('folgt der gespeicherten Wahl und nicht dem Systemdesign', () => {
    expect(startfarbeFuer('dunkel', signale())).toBe('#14161a');
    expect(startfarbeFuer('hell', signale({ bevorzugtDunkel: true }))).toBe('#f7f7f5');
    expect(startfarbeFuer('hochkontrast', signale())).toBe('#000000');
  });

  it('überlässt bei „system“ dem Betriebssystem die Entscheidung', () => {
    expect(startfarbeFuer('system', signale())).toBe('#f7f7f5');
    expect(startfarbeFuer('system', signale({ bevorzugtDunkel: true }))).toBe('#14161a');
  });

  it('nimmt bei erzwungenen Farben die Farben des Betriebssystems', () => {
    /* Dort greift tokens.css auf `Canvas` zurück; ein helles Kontrastdesign
       ist weiß. Eine eigene Farbe wäre hier falsch – auch dann, wenn jemand
       „Dunkel“ eingestellt hat. */
    expect(startfarbeFuer('dunkel', signale({ erzwungeneFarben: true }))).toBe('#ffffff');
    expect(startfarbeFuer('hell', signale({ erzwungeneFarben: true, bevorzugtDunkel: true }))).toBe(
      '#000000',
    );
  });

  /* Dass diese drei Farben mit `--farbe-grund` in tokens.css übereinstimmen,
     prüft `tests/token-kontraste.test.ts` – dort liegt der Leser für die
     CSS-Datei bereits. */
});

describe('umschaltAnsage', () => {
  it('nennt bei "system" das tatsächlich aktive Theme', () => {
    expect(umschaltAnsage('system', 'dunkel')).toContain('Systemvorgabe');
    expect(umschaltAnsage('system', 'dunkel')).toContain('Dunkel');
  });

  it('nennt bei manueller Wahl die gewählte Darstellung', () => {
    expect(umschaltAnsage('hochkontrast', 'hochkontrast')).toBe(
      'Darstellung umgestellt auf Hoher Kontrast.',
    );
  });

  /*
    Läuft das Windows-Kontrastdesign, hat `themeAufloesen` unbedingten
    Vorrang: Die Wahl wird gespeichert, sichtbar wird sie nicht. Bis 0.24.1
    meldete die Ansage trotzdem „Darstellung umgestellt auf Dunkel“ – für
    jemanden, der die Umstellung nicht sehen kann, die irreführendste
    Auskunft, die dort stehen kann.
  */
  it('behauptet bei erzwungenen Farben keine Umstellung, die nicht stattfindet', () => {
    const ansage = umschaltAnsage('dunkel', 'hochkontrast', signale({ erzwungeneFarben: true }));

    expect(ansage).not.toContain('umgestellt');
    expect(ansage).toContain('gespeichert');
    expect(ansage).toContain('Kontrastdesign');
    expect(ansage).toContain('Dunkel');
  });
});
