// @vitest-environment node
// Reiner Main-Prozess-Test; jsdom wird hier nicht gebraucht.

import { EventEmitter } from 'node:events';

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

import { KANAL_ENERGIESPAREN_ENDE } from '../src/shared/ipc';

/**
 * Die Uhr der Prüfungssimulation im Energiesparmodus.
 *
 * Bis 0.22.0 hing die gesamte Zeitrechnung an der Wanduhr: `endeMs` und
 * `verbrauchtMs` leiten sich aus `startMs` ab, und niemand fragte, ob der
 * Rechner zwischendurch schlief. Wer mitten in einem 120-Minuten-Bogen den
 * Laptop zuklappte, verbrauchte die Mittagspause als Bearbeitungszeit – der
 * Bogen war beim Aufwachen abgelaufen, und der Herzschlag schrieb die
 * aufgeblähte Dauer sogar in die Datenbank.
 *
 * Geprüft wird hier die Meldeseite: dass die Schlafdauer überhaupt und
 * richtig gemessen an die Fenster geht. Dass die Oberfläche daraufhin ihren
 * Zeitursprung verschiebt, prüft `renderer-pruefung.test.tsx`.
 */

/** Nachbildung von `powerMonitor` – ein Ereignissender genügt. */
const powerMonitor = new EventEmitter();

/** Was die Fenster zu sehen bekommen. */
let gesendet: { kanal: string; nutzlast: unknown }[] = [];

/** Ob das Fenster sich für zerstört hält – der Randfall beim Beenden. */
let zerstoert = false;

const fenster = {
  isDestroyed: (): boolean => zerstoert,
  webContents: {
    send: (kanal: string, nutzlast: unknown): void => {
      gesendet.push({ kanal, nutzlast });
    },
  },
};

vi.mock('electron', () => ({
  powerMonitor,
  BrowserWindow: {
    getAllWindows: (): unknown[] => [fenster],
  },
}));

const { energieUeberwachen, energieVergessen } = await import('../src/main/energie');

/** Führt einen Schlaf von `dauerMs` vor, ohne wirklich zu warten. */
function schlafen(dauerMs: number): void {
  const beginn = Date.UTC(2026, 7, 29, 10, 0, 0);
  const uhr = vi.spyOn(Date, 'now');
  uhr.mockReturnValue(beginn);
  powerMonitor.emit('suspend');
  uhr.mockReturnValue(beginn + dauerMs);
  powerMonitor.emit('resume');
  uhr.mockRestore();
}

beforeEach(() => {
  gesendet = [];
  zerstoert = false;
  energieUeberwachen();
});

afterEach(() => {
  energieVergessen();
  vi.restoreAllMocks();
});

describe('Energiesparmodus melden', () => {
  it('meldet die Schlafdauer nach dem Aufwachen', () => {
    schlafen(30 * 60 * 1000);

    expect(gesendet).toEqual([{ kanal: KANAL_ENERGIESPAREN_ENDE, nutzlast: 30 * 60 * 1000 }]);
  });

  it('schweigt bei einem Schlaf von Bruchteilen einer Sekunde', () => {
    /* Zwischen `suspend` und `resume` liegt selbst bei einem sofort
       abgebrochenen Standby ein Rest an Zeit. Den gutzuschreiben verschöbe
       den Zeitursprung ohne Anlass und ließe die Ansage grundlos erscheinen. */
    schlafen(200);

    expect(gesendet).toEqual([]);
  });

  it('meldet nichts bei einem Aufwachen ohne vorheriges Einschlafen', () => {
    /* `resume` kann ohne passendes `suspend` eintreffen – etwa wenn die
       Anwendung erst währenddessen gestartet wurde. Eine Gutschrift „seit
       Anbeginn“ wäre der schlimmste Fall: Sie schöbe den Zeitursprung um
       eine willkürliche Menge. */
    powerMonitor.emit('resume');

    expect(gesendet).toEqual([]);
  });

  it('schreibt nicht in ein zerstörtes Fenster', () => {
    zerstoert = true;
    schlafen(30 * 60 * 1000);

    expect(gesendet).toEqual([]);
  });

  it('meldet auch bei einem Uhrsprung zurück nichts Negatives', () => {
    /* Stellt jemand die Systemuhr während des Schlafs zurück, ergäbe die
       Differenz eine negative Dauer. Gutgeschrieben verkürzte sie die
       Restzeit – das Gegenteil der Absicht. */
    const beginn = Date.UTC(2026, 7, 29, 10, 0, 0);
    const uhr = vi.spyOn(Date, 'now');
    uhr.mockReturnValue(beginn);
    powerMonitor.emit('suspend');
    uhr.mockReturnValue(beginn - 60 * 60 * 1000);
    powerMonitor.emit('resume');
    uhr.mockRestore();

    expect(gesendet).toEqual([]);
  });

  it('meldet zwei Pausen einzeln', () => {
    schlafen(10 * 60 * 1000);
    schlafen(5 * 60 * 1000);

    expect(gesendet.map((eintrag) => eintrag.nutzlast)).toEqual([10 * 60 * 1000, 5 * 60 * 1000]);
  });

  it('hängt die Zuhörer nur einmal, auch bei mehrfachem Anmelden', () => {
    /* Ein zweites Anmelden meldete jede Pause doppelt – und die Oberfläche
       schriebe den Zeitursprung zweimal vor. */
    energieUeberwachen();
    energieUeberwachen();

    schlafen(30 * 60 * 1000);

    expect(gesendet).toHaveLength(1);
  });

  it('schweigt nach dem Abmelden', () => {
    energieVergessen();
    schlafen(30 * 60 * 1000);

    expect(gesendet).toEqual([]);
  });

  it('lässt keine Zuhörer zurück', () => {
    energieVergessen();

    expect(powerMonitor.listenerCount('suspend')).toBe(0);
    expect(powerMonitor.listenerCount('resume')).toBe(0);
  });
});
