<?php

declare(strict_types=1);

namespace ViartasThemes\Protera\Support;

/**
 * Locale-aware URL resolver for the legal pages a public form has to link to.
 *
 * SystemFindings/task-18 (finding #22). The register override used to write
 * `href="/privatumo-politika"` and `href="/pirkimo-salygos"` straight into the
 * markup. Two separate defects sat in that one attribute:
 *
 *   1. 🔑 IT IS A LITHUANIAN SLUG ON EVERY LOCALE. `/en/register` linked the
 *      English visitor at the Lithuanian URL, which is not the English page's
 *      address — Pages stores a slug PER LOCALE (`privatumo-politika` /
 *      `privacy-policy`), so the correct link differs per request.
 *   2. It assumes the row exists. A consumer install that never seeded the legal
 *      pages — or an operator who renamed the slug in the admin — gets a 404 out
 *      of the registration form, on the checkbox the visitor is legally required
 *      to tick. `public-page-recipe` §7: guard the link, never emit a dead one.
 *
 * The guard is what this class is for, and it fails to `null`, not to `href="#"`
 * and not to a guessed path. The caller renders plain text when it gets null, so
 * a missing legal page costs a link and never costs a 404.
 *
 * SOFT dependency on Pages, on purpose. Protera requires only `va-core/core` and
 * `va-core/themes`; a storefront without the Pages package still has to render
 * its own registration form. Hence `class_exists()` plus a `\Throwable` catch —
 * this runs during a GET on a public screen, and a legal-page lookup is never
 * worth a 500.
 *
 * The identifier is the DEFAULT-LOCALE slug, because that is what the row is
 * known by everywhere else in this repo (seeders, menus, the wiper's keep-list).
 * The URL that comes back is the current locale's.
 */
final class LegalPageLinks
{
    /**
     * Per-request memo: "{default-slug}|{locale}" => URL or null.
     *
     * The register screen asks for two pages on one render, and a public form is
     * not the place to pay for repeated lookups of rows that cannot change
     * mid-request.
     *
     * @var array<string, string|null>
     */
    private static array $memo = [];

    /**
     * Public URL of the page known by $defaultLocaleSlug, in the current locale.
     *
     * Returns null when the page does not exist, is not published, or has no slug
     * in this locale — all three of which mean the same thing to a visitor, and
     * none of which may produce a link.
     *
     * @param  string  $defaultLocaleSlug  Slug in `cms.default_locale`, e.g. `privatumo-politika`.
     */
    public static function url(string $defaultLocaleSlug): ?string
    {
        $locale = app()->getLocale();
        $memoKey = $defaultLocaleSlug.'|'.$locale;

        if (array_key_exists($memoKey, self::$memo)) {
            return self::$memo[$memoKey];
        }

        return self::$memo[$memoKey] = self::resolve($defaultLocaleSlug, $locale);
    }

    /**
     * Forget everything resolved so far.
     *
     * Only for tests, which switch locale and seed pages inside one process.
     */
    public static function flush(): void
    {
        self::$memo = [];
    }

    /**
     * The lookup itself, without the memo.
     */
    private static function resolve(string $defaultLocaleSlug, string $locale): ?string
    {
        /** @var class-string|string $page */
        $page = 'ViartasModules\\Pages\\Models\\Page';

        if (! class_exists($page)) {
            return null;
        }

        try {
            // NO `@var \ViartasModules\Pages\Models\Page` annotation here, and no
            // import: Pint's fully_qualified_strict_types fixer HOISTS an FQCN out
            // of a docblock into a real `use` statement (Pamoka #13.4), and a `use`
            // pointing at the package this class exists to stay optional about is a
            // documentation lie even though PHP never resolves it. The shape is a
            // Page; the type system is deliberately not told.
            /** @var object|null $row */
            $row = $page::query()
                ->published()
                ->bySlug($defaultLocaleSlug, config('cms.default_locale', 'lt'))
                ->first();

            if ($row === null) {
                return null;
            }

            // No slug in THIS locale means the page is not addressable here —
            // getUrl() would happily hand back `/en/` (the locale root), which is
            // a link to the homepage dressed up as a privacy policy.
            if ($row->getFullSlug($locale) === '') {
                return null;
            }

            return $row->getUrl($locale);
        } catch (\Throwable) {
            // No database, no `pages` table (fresh install mid-migration), or the
            // module removed from under us. A registration form must still render.
            return null;
        }
    }
}
