<?php

use Illuminate\Support\Facades\File;

/**
 * The public website is one of the three surfaces the token file describes.
 *
 * It was the last one still speaking the old language: a 900-line page with its
 * own design system inside a <style> block — warm paper, a serif headline, 3px
 * corners, its own scale of greys — written before the tokens existed. A
 * visitor who clicked "Anmelden" left one company and arrived at another.
 *
 * These tests are about drift, not about looks. Nothing stops someone pasting a
 * hex value into a marketing page at four in the afternoon; what stops it
 * becoming a second design system is noticing on the first one.
 */

/** Public pages that must draw from the shared stylesheet. */
function sitePages(): array
{
    return [
        'landing.blade.php',
        'legal.blade.php',
        'status.blade.php',
        'components/layouts/site.blade.php',
    ];
}

it('builds the public site from the shared tokens, not from its own palette', function (string $page) {
    $source = File::get(resource_path('views/'.$page));

    // Hex literals are the tell. One is a paste; a dozen is a second design
    // system growing quietly beside the first.
    preg_match_all('/#[0-9a-fA-F]{3,8}\b/', $source, $found);

    expect($found[0])->toBeEmpty()
        // The old page set its own type stack. The token file owns that now,
        // and a serif headline is precisely the impression the redesign
        // removed — every page read as a document.
        ->and($source)->not->toContain('font-family')
        ->and($source)->not->toContain('Georgia')
        ->and($source)->not->toContain('font-serif');
})->with(sitePages());

it('serves the website from the same stylesheet as the portal', function () {
    // Not "a stylesheet exists" — the SAME one. Two builds would drift the
    // moment a token changed in one of them.
    $layout = File::get(resource_path('views/components/layouts/site.blade.php'));

    expect($layout)->toContain('<x-shell.head');

    $head = File::get(resource_path('views/components/shell/head.blade.php'));

    expect($head)->toContain("@vite(['resources/css/app.css'");
});

it('keeps the deliberately self-contained page self-contained', function () {
    // The exception, named rather than forgotten. coming-soon is shown when the
    // application may not be able to serve a built stylesheet at all — mid
    // deploy, or freshly installed — and @vite would throw where the whole
    // point is to render something reassuring.
    //
    // The status page used to be on this list on the same theory and does not
    // belong: if the stylesheet cannot be served, the application serving the
    // status page is already failing, and mid-deployment every route answers
    // with the maintenance page. What the exemption actually bought was a third
    // design nobody maintained.
    $source = File::get(resource_path('views/coming-soon.blade.php'));

    // The CALL, not the word: the file explains in a comment why it does not
    // use @vite, and a plain substring match reads that explanation as the
    // offence it warns about.
    expect($source)->not->toMatch('/@vite\s*\(/')
        ->and($source)->toContain('<style>');
});

it('does not leave the old marketing stylesheet behind anywhere', function () {
    // The tell of the previous edition: warm paper and the 3px radius. Left in
    // a partial nobody opened, it would come back the next time somebody
    // copied a section.
    foreach (File::allFiles(resource_path('views')) as $file) {
        if ($file->getFilename() === 'coming-soon.blade.php') {
            continue;
        }

        // The mail layout is deliberately inline-styled: an email client has no
        // stylesheet of ours to load.
        if (str_contains($file->getPathname(), '/mail/') || str_contains($file->getPathname(), '/pdf/')) {
            continue;
        }

        expect(File::get($file->getPathname()))
            ->not->toContain('--paper')
            ->not->toContain('Plex Serif');
    }
});

it('does not space the wordmark out into separate words', function () {
    // It went out reading "Clu Pilot Cloud". `gap` on a flex container applies
    // between EVERY child, and a bare text node is a child — so an inline-flex
    // row wrapped around "Clu", <i>Pilot</i> and " Cloud" put nine pixels
    // between each of them.
    //
    // The rule that prevents it: the mark and the wordmark are the only two
    // children of the flex row, and the wordmark is one element.
    $source = File::get(resource_path('views/coming-soon.blade.php'));

    expect($source)->toContain('<span class="word">CluPilot')
        ->and($source)->not->toContain('Clu<i>Pilot</i>');
});

it('draws the actual mark on the placeholder, not an empty tile', function () {
    // It was a plain gradient square. A brand tile with nothing in it reads as
    // an image that failed to load — on a "we are still building" page that is
    // the last impression to leave.
    //
    // Inlined rather than pulled from x-ui.logo: this page renders when the
    // asset build may not exist, and a Blade component is fine but the SVG has
    // to be in the file either way.
    $source = File::get(resource_path('views/coming-soon.blade.php'));

    expect($source)->toContain('viewBox="0 0 64 64"')
        // The ascent triangle — the part that makes it a mark rather than a
        // rounded rectangle.
        ->and($source)->toContain('M32 17 44 44l-12-6-12 6z');
});

it('draws the wordmark from one definition, not five', function () {
    // It had five: the site header set it in ink, the sidebar split it into
    // "Clu" plus an orange "Pilot", the sign-in plate set it white at another
    // size, the placeholder inlined its own, and the maintenance screen had no
    // wordmark at all. Five surfaces of one product, each with its own idea of
    // the brand.
    $offenders = [];

    foreach (File::allFiles(resource_path('views')) as $file) {
        // The two self-contained pages cannot use a Blade component's compiled
        // output — they render when the asset build may not exist — and the
        // component itself obviously spells the word out.
        // The self-contained pages cannot use a Blade component's compiled
        // output — they render when the asset build may not exist — and an
        // email has no stylesheet of ours at all, so its lockup is inline by
        // necessity. They still have to AGREE with the component, which is
        // what the next assertion checks.
        if (in_array($file->getFilename(), ['coming-soon.blade.php', 'updating-panel.blade.php', 'brand.blade.php', 'layout.blade.php'], true)) {
            continue;
        }

        $source = preg_replace('/\{\{--.*?--\}\}/s', '', File::get($file->getPathname()));

        // The wordmark is markup AROUND the name: the name is the whole content
        // of its element, so what follows it is the next tag and not more
        // sentence. `>Clu<` is the split form, `>CluPilot` up to a tag is the
        // whole one.
        //
        // Twice now a document has tripped a looser version of this — the terms
        // page and then the processing agreement, both with a paragraph whose
        // first word happens to be the company name. That is a sentence, not a
        // brand lockup, and a scan that cannot tell them apart is a scan that
        // gets edited every time somebody writes prose.
        if (preg_match('/>Clu(<|Pilot\s*<)/', $source)) {
            $offenders[] = str_replace(resource_path().'/', '', $file->getPathname());
        }
    }

    expect($offenders)->toBeEmpty();

    // The four that must inline it write the name in one piece, in ink, with
    // no orange half — the shape the component defines.
    foreach ([
        'coming-soon.blade.php',
        'partials/updating-panel.blade.php',
        'errors/layout.blade.php',
        'components/mail/layout.blade.php',
    ] as $page) {
        expect(File::get(resource_path('views/'.$page)))
            ->toContain('CluPilot')
            ->not->toContain('Clu<i>Pilot</i>')
            ->not->toContain('>Clu</span>');
    }
});

it('gives the maintenance screen the same typeface as everything else', function () {
    // The console loads IBM Plex through Vite. The 503 page has no stylesheet
    // at all, so the maintenance screen — the one page shown when somebody is
    // already worried — was rendering in the system sans.
    $panel = File::get(resource_path('views/partials/updating-panel.blade.php'));

    expect($panel)->toContain('@font-face')
        ->and($panel)->toContain('/fonts/ibm-plex-sans-latin-700-normal.woff2');

    // And the file it names is actually there. A @font-face pointing at
    // nothing fails silently, which is how the bold weight went missing from
    // the placeholder without anyone noticing.
    foreach (['400', '500', '600', '700'] as $weight) {
        expect(public_path("fonts/ibm-plex-sans-latin-{$weight}-normal.woff2"))->toBeFile();
    }
});

it('names the address zone the installation is configured for', function () {
    // It said "Adresse auf clupilot.cloud" — a domain the company does not own
    // — while provisioning issued addresses under whatever CLUPILOT_DNS_ZONE
    // said. The same two-sources-of-truth mistake as a hard-coded price.
    App\Support\Settings::set('provisioning.dns_zone', 'example.test');

    // Comments stripped: the controller explains at length that it used to
    // name clupilot.cloud, and a raw scan reads the explanation as the
    // offence. Third time this suite has had to learn it.
    $source = implode('', array_map(
        fn ($token) => is_array($token) && in_array($token[0], [T_COMMENT, T_DOC_COMMENT], true) ? '' : (is_array($token) ? $token[1] : $token),
        token_get_all(File::get(app_path('Http/Controllers/LandingController.php'))),
    ));

    expect($source)->not->toContain('clupilot.cloud');

    $page = $this->get('/');

    $page->assertOk();

    // Only where the catalogue actually sells the feature; either way the old
    // literal must be gone.
    $page->assertDontSee('clupilot.cloud');
});
