/**
 * @jest-environment jsdom
 */

const fs = require("fs");
const path = require("path");

describe("calculate() row223 Ellbow-Rotation Tests", () => {

    let calculate;

    beforeEach(() => {
        // DOM erzeugen
        document.body.innerHTML = `
            <textarea id="analysis-log"></textarea>
        `;

        // Fetch mocken - wird pro Test konfiguriert
        global.fetch = jest.fn();

        // Modul erst JETZT laden (DOM existiert)
        ({ calculate } = require("../public/calculateActions.js"));
    });

    /**
     * Hilfsfunktion: Erstellt ein CSV mit definierten row223.x_mm und x226 Wert
     * @param {number} row223_x_mm - x_mm Wert für row223 (id=223)
     * @param {number} x226_base - Basis-x_mm Wert für x226-Punkt (wird durch mehrere Punkte erreicht)
     */
    function createTestCSV(row223_x_mm, x226_base) {
        const csvContent = `id,x_mm,y_mm,z_mm,roll_deg,pitch_deg,seen_by
222,${x226_base},120,65,0,0,3
226,${x226_base},115,61,0,0,3
229,${x226_base - 90},110,55,0,0,3
223,${row223_x_mm},125,62,0,0,3
`;
        return csvContent;
    }

    /**
     * Berechnet den erwarteten Winkel basierend auf Formel aus calculateActions.js
     * angleRad = Math.asin(dx / 35)
     * angleDeg = 90 - angleRad * 180 / Math.PI
     */
    function calculateExpectedAngle(row223_x_mm, x226) {
        const dx = row223_x_mm - x226;
        if (Math.abs(dx) > 35) {
            return null; // Math.asin() erlaubt nur Werte zwischen -1 und 1
        }
        const angleRad = Math.asin(dx / 35);
        const angleDeg = 90 - angleRad * 180 / Math.PI;
        return { angleRad, angleDeg, dx };
    }

    // Test 1: row223 mit x_mm = x226 (dx = 0, erwarteter Winkel = 90°)
    test("row223.x_mm = x226 sollte Winkel von 90° ergeben", async () => {
        const x226_base = 100;
        const row223_x_mm = x226_base; // dx = 0
        const csvContent = createTestCSV(row223_x_mm, x226_base);

        global.fetch.mockResolvedValueOnce({
            ok: true,
            headers: { get: () => "text/csv" },
            text: async () => csvContent
        });

        const result = await calculate();

        // xCount sollte > 2 sein (wir haben 4 Zeilen)
        expect(result.status).toBe("ok");

        // Überprüfe, ob die elbowRotationAFromX223 Berechnung vorhanden ist
        const elbowRotationCalc = result.calculations.find(
            c => c.type === "elbowRotationAFromX223"
        );

        expect(elbowRotationCalc).toBeDefined();
        expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(90, 1);
        expect(elbowRotationCalc.output.angleRad).toBeCloseTo(0, 4);
    });

    // Test 2: row223 mit x_mm = x226 + 17.5 (dx = 17.5, erwarteter Winkel = 45°)
    test("row223.x_mm = x226 + 17.5 sollte Winkel von 45° ergeben", async () => {
        const x226_base = 100;
        const row223_x_mm = x226_base + 17.5; // dx = 17.5
        const csvContent = createTestCSV(row223_x_mm, x226_base);

        global.fetch.mockResolvedValueOnce({
            ok: true,
            headers: { get: () => "text/csv" },
            text: async () => csvContent
        });

        const result = await calculate();
        const expected = calculateExpectedAngle(row223_x_mm, x226_base);

        expect(result.status).toBe("ok");

        const elbowRotationCalc = result.calculations.find(
            c => c.type === "elbowRotationAFromX223"
        );

        expect(elbowRotationCalc).toBeDefined();
        expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1);
        expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3);
    });

    // Test 3: row223 mit x_mm = x226 + 35 (dx = 35, erwarteter Winkel = 0°)
    test("row223.x_mm = x226 + 35 sollte Winkel von 0° ergeben", async () => {
        const x226_base = 100;
        const row223_x_mm = x226_base + 35; // dx = 35, asin(1) = π/2
        const csvContent = createTestCSV(row223_x_mm, x226_base);

        global.fetch.mockResolvedValueOnce({
            ok: true,
            headers: { get: () => "text/csv" },
            text: async () => csvContent
        });

        const result = await calculate();
        const expected = calculateExpectedAngle(row223_x_mm, x226_base);

        expect(result.status).toBe("ok");

        const elbowRotationCalc = result.calculations.find(
            c => c.type === "elbowRotationAFromX223"
        );

        expect(elbowRotationCalc).toBeDefined();
        expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1);
        expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3);
    });

    // Test 4: row223 mit negativem dx (x_mm = x226 - 17.5, erwarteter Winkel = 135°)
    test("row223.x_mm = x226 - 17.5 sollte Winkel von 135° ergeben", async () => {
        const x226_base = 100;
        const row223_x_mm = x226_base - 17.5; // dx = -17.5
        const csvContent = createTestCSV(row223_x_mm, x226_base);

        global.fetch.mockResolvedValueOnce({
            ok: true,
            headers: { get: () => "text/csv" },
            text: async () => csvContent
        });

        const result = await calculate();
        const expected = calculateExpectedAngle(row223_x_mm, x226_base);

        expect(result.status).toBe("ok");

        const elbowRotationCalc = result.calculations.find(
            c => c.type === "elbowRotationAFromX223"
        );

        expect(elbowRotationCalc).toBeDefined();
        expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1);
        expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3);
    });

    // Test 5: row223 mit x_mm = x226 - 35 (dx = -35, erwarteter Winkel = 180°)
    test("row223.x_mm = x226 - 35 sollte Winkel von 180° ergeben", async () => {
        const x226_base = 100;
        const row223_x_mm = x226_base - 35; // dx = -35, asin(-1) = -π/2
        const csvContent = createTestCSV(row223_x_mm, x226_base);

        global.fetch.mockResolvedValueOnce({
            ok: true,
            headers: { get: () => "text/csv" },
            text: async () => csvContent
        });

        const result = await calculate();
        const expected = calculateExpectedAngle(row223_x_mm, x226_base);

        expect(result.status).toBe("ok");

        const elbowRotationCalc = result.calculations.find(
            c => c.type === "elbowRotationAFromX223"
        );

        expect(elbowRotationCalc).toBeDefined();
        expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expected.angleDeg, 1);
        expect(elbowRotationCalc.output.angleRad).toBeCloseTo(expected.angleRad, 3);
    });

    // Test 6: xCount > 2 Bedingung wird erfüllt
    test("xCount muss > 2 sein, damit elbowRotationAFromX223 berechnet wird", async () => {
        const x226_base = 100;
        const row223_x_mm = x226_base + 10;
        const csvContent = createTestCSV(row223_x_mm, x226_base);

        global.fetch.mockResolvedValueOnce({
            ok: true,
            headers: { get: () => "text/csv" },
            text: async () => csvContent
        });

        const result = await calculate();

        // Überprüfe, dass die Berechnung stattgefunden hat
        const elbowXEstimate = result.calculations.find(
            c => c.type === "elbowXEstimate"
        );
        const elbowRotationCalc = result.calculations.find(
            c => c.type === "elbowRotationAFromX223"
        );

        expect(elbowXEstimate).toBeDefined(); // xCount wurde berechnet
        expect(elbowRotationCalc).toBeDefined(); // xCount > 2 und row223 existiert
    });

    // Test 7: Parametrisierter Test mit verschiedenen Winkeln
    test.each([
        { dx: 0, expectedDeg: 90, desc: "dx=0" },
        { dx: 5, expectedDeg: 90 - Math.asin(5 / 35) * 180 / Math.PI, desc: "dx=5" },
        { dx: 10, expectedDeg: 90 - Math.asin(10 / 35) * 180 / Math.PI, desc: "dx=10" },
        { dx: -5, expectedDeg: 90 - Math.asin(-5 / 35) * 180 / Math.PI, desc: "dx=-5" },
        { dx: -10, expectedDeg: 90 - Math.asin(-10 / 35) * 180 / Math.PI, desc: "dx=-10" },
    ])(
        "Verschiedene dx Werte sollten korrekte Winkel ergeben ($desc)",
        async ({ dx, expectedDeg }) => {
            const x226_base = 100;
            const row223_x_mm = x226_base + dx;
            const csvContent = createTestCSV(row223_x_mm, x226_base);

            global.fetch.mockResolvedValueOnce({
                ok: true,
                headers: { get: () => "text/csv" },
                text: async () => csvContent
            });

            const result = await calculate();

            const elbowRotationCalc = result.calculations.find(
                c => c.type === "elbowRotationAFromX223"
            );

            expect(elbowRotationCalc).toBeDefined();
            expect(elbowRotationCalc.output.angleDeg).toBeCloseTo(expectedDeg, 1);
        }
    );

    // Test 8: row223 Input wird korrekt in Output dokumentiert
    test("row223.x_mm wird korrekt in Berechnung dokumentiert", async () => {
        const x226_base = 100;
        const row223_x_mm = 115;
        const csvContent = createTestCSV(row223_x_mm, x226_base);

        global.fetch.mockResolvedValueOnce({
            ok: true,
            headers: { get: () => "text/csv" },
            text: async () => csvContent
        });

        const result = await calculate();

        const elbowRotationCalc = result.calculations.find(
            c => c.type === "elbowRotationAFromX223"
        );

        expect(elbowRotationCalc).toBeDefined();
        expect(elbowRotationCalc.input.x223).toBe(row223_x_mm);
        expect(elbowRotationCalc.input.x226).toBe(x226_base);
    });
});
