<?php

use Illuminate\Support\Facades\File;

/**
 * R20 — editing with fields happens in a modal, never in the row.
 *
 * Inline editing was tried once, on the seats table. It worked and it looked
 * broken: the row grew as the inputs appeared, the columns beside it jumped,
 * and a table half in edit mode reads as a rendering fault rather than as a
 * form. The project already had a modal for exactly this (EditDatacenter);
 * the seats table simply did not use it.
 *
 * So the rule is mechanical: a page view that would grow input fields into a
 * table row fails this test. Forms that ARE the page — a create form, a
 * settings panel, the invite row above a table — are not editing an existing
 * record in place and are untouched by this.
 */

/** Views that legitimately hold their own fields: they are forms, not rows. */
function isModalOrForm(string $path): bool
{
    return str_contains($path, '/components/')
        || preg_match('#/livewire/(admin/)?(edit|confirm|create|new)-#', $path) === 1
        || preg_match('#/(auth|errors|mail)/#', $path) === 1;
}

it('never grows input fields inside a table row', function () {
    $offenders = [];

    foreach (File::allFiles(resource_path('views')) as $file) {
        $path = str_replace(base_path().'/', '', $file->getPathname());

        if (isModalOrForm($path)) {
            continue;
        }

        $source = $file->getContents();

        // Look only INSIDE <td>…</td>: a field in a table cell is an inline
        // editor by definition. Fields elsewhere on the page are a form.
        foreach (preg_split('#</td>#', $source) as $segment) {
            $open = strrpos($segment, '<td');

            // No cell opened in this segment — everything before the first
            // <td> is page, not row. Without this the whole file counted as
            // one cell and every ordinary form failed.
            if ($open === false) {
                continue;
            }

            $cell = substr($segment, $open);

            if (preg_match('#<(input|textarea)\b(?![^>]*type="(checkbox|radio|hidden)")#', $cell, $m)) {
                $offenders[$path][] = trim(strtok(ltrim($cell), "\n") ?: $m[0]);
            }
        }

        // Und dasselbe in <li>…</li>.
        //
        // Nachgetragen am 31.7.2026, weil genau hier ein Verstoß durchrutschte:
        // die Liste der geplatzten Zahlungen trug je Zeile ein Textfeld für die
        // Begründung und einen Knopf, der eine Methode am Seiten-Bauteil rief.
        // Beides steht wörtlich in R20 unter „Verboten" — die Prüfung sah nur
        // <td> und damit den Buchstaben statt des Sinns.
        //
        // Checkbox, Radio und <select> bleiben auch hier erlaubt: ein Klick,
        // ein Wert, keine Höhenänderung (R20s eigene Ausnahme). Getroffen wird
        // nur, was eine Zeile wirklich wachsen lässt.
        foreach (preg_split('#</li>#', $source) as $segment) {
            $open = strrpos($segment, '<li');

            if ($open === false) {
                continue;
            }

            $row = substr($segment, $open);

            if (preg_match('#<(input|textarea)\b(?![^>]*type="(checkbox|radio|hidden)")|<x-ui\.input\b#', $row, $m)) {
                $offenders[$path][] = trim(strtok(ltrim($row), "\n") ?: $m[0]);
            }
        }
    }

    expect($offenders)->toBe([]);
});

it('reaches every edit modal through openModal', function () {
    // An edit button that calls a method on the page component instead of
    // dispatching openModal is the inline editor coming back under a new name.
    $modals = collect(File::allFiles(app_path('Livewire')))
        ->filter(fn ($f) => str_contains($f->getContents(), 'extends ModalComponent'))
        ->map(fn ($f) => $f->getFilenameWithoutExtension())
        ->values();

    expect($modals)->toContain('EditSeat')
        ->and($modals)->toContain('EditDatacenter');

    // And the seats table actually opens it.
    $users = File::get(resource_path('views/livewire/users.blade.php'));

    expect($users)->toContain("component: 'edit-seat'");
});

it('closes every modal through the event the modal actually listens for', function () {
    // Alpine's $dispatch fires a browser DOM event; wire-elements/modal listens
    // for a LIVEWIRE event of that name. A cancel button written the Alpine way
    // therefore does nothing at all — reported on the mail-template modal, where
    // saving worked and cancelling did not. Every other modal in the repo was
    // already correct, which is exactly why nobody noticed the odd one out.
    $offenders = collect(File::allFiles(resource_path('views')))
        ->filter(fn ($file) => str_ends_with($file->getFilename(), '.blade.php'))
        ->filter(function ($file) {
            // Blade comments stripped first: this file's own explanation of the
            // wrong form must not read as an offence.
            $body = preg_replace('/\{\{--.*?--\}\}/s', '', $file->getContents());

            // ALPINE's $dispatch only. wire:click="$dispatch('closeModal')" is
            // Livewire's own dispatcher and works — the broken form is the one
            // bound through an Alpine handler, where $dispatch means the DOM.
            return preg_match(
                '/(?:x-on:click|@click)\s*=\s*"[^"]*\$dispatch\(\s*[\'"]closeModal/',
                (string) $body,
            ) === 1;
        })
        ->map(fn ($file) => $file->getRelativePathname())
        ->values()
        ->all();

    expect($offenders)->toBe([]);

    // And the working form is the one in use.
    expect(File::get(resource_path('views/livewire/admin/edit-mail-template.blade.php')))
        ->toContain("Livewire.dispatch('closeModal')");
});
