/**
 * Tests for shared/diff.js — the dependency-free text diff used by the
 * browse tool's markdown version-history viewer.
 *
 * Runs against the same shim as zddc.spec.js (shared/zddc-test.html,
 * which loads shared/diff.js and exposes window.zddc.diff).
 */

import { test, expect } from '@playwright/test';
import * as path from 'path';

const SHIM_PATH = 'file://' + path.resolve('shared/zddc-test.html');

async function diff(page, fn, ...args) {
    return page.evaluate(
        ([fn, args]) => window.zddc.diff[fn](...args),
        [fn, args]
    );
}

test.beforeEach(async ({ page }) => {
    await page.goto(SHIM_PATH, { waitUntil: 'load' });
});

test('diff module is attached to window.zddc', async ({ page }) => {
    const present = await page.evaluate(() =>
        !!(window.zddc && window.zddc.diff &&
           typeof window.zddc.diff.lines === 'function' &&
           typeof window.zddc.diff.words === 'function'));
    expect(present).toBe(true);
});

test('identical text produces only eq ops', async ({ page }) => {
    const ops = await diff(page, 'lines', 'a\nb\nc', 'a\nb\nc');
    expect(ops.every(o => o.type === 'eq')).toBe(true);
});

test('a changed middle line shows del then add', async ({ page }) => {
    const ops = await diff(page, 'lines', 'a\nb\nc', 'a\nB\nc');
    const compact = ops.map(o => `${o.type}:${o.text}`).join('|');
    expect(compact).toContain('eq:a');
    expect(compact).toContain('del:b');
    expect(compact).toContain('add:B');
    expect(compact).toContain('eq:c');
});

test('stats count added and removed lines', async ({ page }) => {
    const addStats = await diff(page, 'stats', await diff(page, 'lines', 'a\nb', 'a\nx\nb'));
    expect(addStats).toEqual({ added: 1, removed: 0 });

    const delStats = await diff(page, 'stats', await diff(page, 'lines', 'a\nb\nc', 'a\nc'));
    expect(delStats).toEqual({ added: 0, removed: 1 });
});

test('pure insertion at end', async ({ page }) => {
    const ops = await diff(page, 'lines', 'one\ntwo', 'one\ntwo\nthree');
    const added = ops.filter(o => o.type === 'add').map(o => o.text);
    expect(added).toEqual(['three']);
    expect(ops.filter(o => o.type === 'del')).toHaveLength(0);
});

test('word diff aligns on word boundaries, preserving spaces', async ({ page }) => {
    const ops = await diff(page, 'words', 'the quick fox', 'the slow fox');
    const changed = ops.filter(o => o.type !== 'eq').map(o => `${o.type}:${o.text}`);
    expect(changed).toEqual(['del:quick', 'add:slow']);
});

test('empty inputs do not throw', async ({ page }) => {
    const ops = await diff(page, 'lines', '', '');
    expect(Array.isArray(ops)).toBe(true);
});
