<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\OAuth;

/**
 * Renders the OAuth authorize/consent screen (and the un-authenticated login-prompt variant) as a
 * single self-contained HTML string, styled to match admin/templates/bootstrap/login.tpl: same
 * `.vertical-center` > `.container` > `#login_wrapper` > `#login_outer.card` > `.card-body`
 * structure, same `.brand-logo` treatment for the header image, same `.btn`/`.btn-primary` button
 * classes. Reuses the shop's real admin CSS bundle ({@see ConsentBranding::adminStyleTags()}) so
 * it tracks the shop's active admin theme; a tiny inline fallback keeps the page usable even if
 * that bundle can't be reached.
 *
 * Plain PHP string templating on purpose — the plugin has no HTML templating layer for this
 * front-controller route (OAuthController already built its previous page this way), and this
 * stays a self-contained OAuth-area file with no new composer dependency.
 */
final class ConsentTemplate
{
    /**
     * @param list<string> $scopes
     */
    public static function consent(
        string $lang,
        string $shopName,
        ?string $logoUrl,
        string $clientName,
        array $scopes,
        string $formAction,
        string $hiddenFieldsHtml,
    ): string {
        $t = static fn (string $key): string => \htmlspecialchars(ConsentLang::t($lang, $key), \ENT_QUOTES);

        $scopeItems = '';
        foreach ($scopes as $scope) {
            $label = \htmlspecialchars(ConsentLang::scopeLabel($lang, $scope), \ENT_QUOTES);
            $desc  = ConsentLang::scopeDescription($lang, $scope);
            $class = 'consent-badge' . (ConsentLang::scopeIsWrite($scope) ? ' consent-badge--write' : '');

            // A scope with no description gets no tooltip and no focus stop: there would be
            // nothing to reveal, and a badge that swallows a tab press without showing anything is
            // worse than a plain one.
            // No `title`: the browser's own tooltip would fire alongside the styled one. The
            // description goes to assistive tech through aria-label instead, so it is never
            // hover-only information.
            $tip = $desc === ''
                ? ''
                : ' tabindex="0" data-tip="' . \htmlspecialchars($desc, \ENT_QUOTES)
                    . '" aria-label="' . \htmlspecialchars(
                        ConsentLang::scopeLabel($lang, $scope) . ' — ' . $desc,
                        \ENT_QUOTES
                    ) . '"';

            $scopeItems .= '<li class="' . $class . '"' . $tip . '>' . $label . '</li>';
        }

        $intro = \sprintf($t('consentIntro'), \htmlspecialchars($clientName, \ENT_QUOTES));

        $body = '<div class="consent-client">' . $intro . '</div>'
            . '<div class="consent-scopes-label">' . $t('scopesLabel') . '</div>'
            . ($scopeItems === ''
                ? ''
                : '<ul class="consent-scope-list">' . $scopeItems . '</ul>')
            // POST, not GET: the decision must not be reachable by a cross-site top-level
            // navigation carrying the ambient admin cookie. See {@see ConsentNonce}.
            . '<form method="post" action="' . \htmlspecialchars($formAction, \ENT_QUOTES) . '">'
            . $hiddenFieldsHtml
            . '<button type="submit" name="approve" value="yes" class="btn btn-primary btn-block mt-3">'
            . $t('approve') . '</button>'
            . '<button type="submit" name="approve" value="no" class="btn btn-default btn-block mt-2">'
            . $t('deny') . '</button>'
            . '</form>'
            . '<p class="consent-footnote text-center mt-3"><small>' . $t('signedInAs') . '</small></p>';

        return self::page($lang, $t('consentPageTitle'), $shopName, $logoUrl, $body);
    }

    public static function loginPrompt(
        string $lang,
        string $shopName,
        ?string $logoUrl,
        string $loginUrl,
        string $returnTo,
    ): string {
        $t = static fn (string $key): string => \htmlspecialchars(ConsentLang::t($lang, $key), \ENT_QUOTES);

        $body = '<p class="text-center">' . $t('loginPromptBody') . '</p>'
            . '<p class="text-center"><a class="btn btn-primary btn-block mt-2" href="'
            . \htmlspecialchars($loginUrl, \ENT_QUOTES) . '">' . $t('loginPromptButton') . '</a></p>'
            . '<p class="consent-footnote text-center mt-3 mb-2"><small>' . $t('loginPromptFooter') . '</small></p>'
            . '<div class="consent-return-url"><code>' . \htmlspecialchars($returnTo, \ENT_QUOTES) . '</code></div>';

        return self::page($lang, $t('loginPromptTitle'), $shopName, $logoUrl, $body, $t('loginPromptHeading'));
    }

    private static function page(
        string $lang,
        string $title,
        string $shopName,
        ?string $logoUrl,
        string $body,
        ?string $heading = null,
    ): string {
        $htmlLang    = $lang === 'en' ? 'en' : 'de';
        $safeTitle   = \htmlspecialchars($title, \ENT_QUOTES);
        $safeShop    = \htmlspecialchars($shopName, \ENT_QUOTES);
        $styleTags   = ConsentBranding::adminStyleTags();
        $headingText = $heading !== null ? \htmlspecialchars($heading, \ENT_QUOTES) : $safeTitle;

        // Show the logo when the shop has uploaded one (Shop::getLogo()), and always show the
        // shop's name too — per spec both must appear, not just whichever is available.
        $logoHtml = $logoUrl !== null
            ? '<img class="brand-logo" src="' . \htmlspecialchars($logoUrl, \ENT_QUOTES)
                . '" alt="' . $safeShop . '">'
            : '';
        $logoHtml .= '<span class="consent-shop-name">' . $safeShop . '</span>';

        // Minimal fallback styling, used verbatim when the real admin bundle can't be loaded, and
        // layered underneath it otherwise (the admin bundle's own rules win via source order).
        $fallbackCss = '.vertical-center{display:flex;flex-direction:column;justify-content:center;'
            . 'min-height:100vh;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Arial,'
            . 'sans-serif;background:#f4f5f7;margin:0}'
            . '.container{width:100%;max-width:420px;margin:0 auto;padding:0 1rem}'
            . '#login_wrapper{max-width:360px;margin:0 auto}'
            . '#login_outer.card{border:1px solid #e2e4e8;border-radius:.5rem;background:#fff;'
            . 'box-shadow:0 2px 10px rgba(0,0,0,.06)}'
            . '.card-body{padding:2rem}'
            . '.brand-logo{max-width:220px;max-height:64px;display:block;margin:0 auto 1.25rem}'
            . '.consent-shop-name{display:block;text-align:center;font-size:1.25rem;font-weight:600;'
            . 'margin-bottom:1.25rem;color:#1f2937}'
            . 'h1.consent-heading{font-size:1.05rem;text-align:center;margin:0 0 1rem;color:#1f2937}'
            . '.consent-client{text-align:center;margin-bottom:1rem;color:#1f2937}'
            . '.consent-scopes-label{font-weight:600;font-size:.85rem;text-transform:uppercase;'
            . 'letter-spacing:.03em;color:#6b7280;margin-bottom:.5rem}'
            // Badges, not a list of rows: fourteen scopes as full-width rows made the card taller
            // than the viewport, and the merchant scrolled past the buttons they came to press.
            // The tooltip is absolutely positioned against the *list*, not the badge, so it spans
            // the card and can never be clipped by a badge sitting at the edge.
            . '.consent-scope-list{position:relative;list-style:none;display:flex;flex-wrap:wrap;'
            . 'gap:.35rem;padding:0;margin:0 0 1rem}'
            . '.consent-badge{display:inline-block;padding:.25rem .6rem;border-radius:999px;'
            . 'font-size:.8rem;line-height:1.35;background:#eef2ff;color:#3730a3;'
            . 'border:1px solid #dfe4ff;cursor:help}'
            . '.consent-badge--write{background:#fff7ed;color:#9a3412;border-color:#fed7aa}'
            . '.consent-badge[data-tip]::after{content:attr(data-tip);position:absolute;'
            . 'left:0;right:0;top:calc(100% + .4rem);z-index:10;padding:.55rem .7rem;'
            . 'border-radius:.4rem;background:#1f2937;color:#f9fafb;font-size:.78rem;'
            . 'line-height:1.45;text-align:left;white-space:normal;'
            . 'box-shadow:0 4px 14px rgba(0,0,0,.18);opacity:0;visibility:hidden;'
            . 'transition:opacity .12s ease-in-out}'
            . '.consent-badge[data-tip]:hover::after,.consent-badge[data-tip]:focus::after{'
            . 'opacity:1;visibility:visible}'
            . '.consent-badge[data-tip]:focus{outline:2px solid #2563eb;outline-offset:1px}'
            . '.consent-footnote{color:#6b7280}'
            . '.consent-return-url{margin:0;padding:.6rem .75rem;background:#f4f5f7;'
            . 'border:1px solid #e2e4e8;border-radius:.4rem;text-align:left;font-size:.78rem;'
            . 'line-height:1.5;color:#374151;word-break:break-all;overflow-wrap:anywhere}'
            . '.consent-return-url code{background:none;padding:0;color:inherit;font-size:inherit;'
            . 'word-break:break-all}'
            . '.btn{display:inline-block;width:100%;padding:.6rem 1rem;border:0;border-radius:.4rem;'
            . 'font-size:1rem;cursor:pointer;text-decoration:none;text-align:center}'
            . '.btn-primary{background:#2563eb;color:#fff}'
            . '.btn-default{background:#e5e7eb;color:#1f2937}'
            // Self-contained button spacing so we never depend on the admin bundle's mt-*/btn-block
            // utilities being present on this frontend-dispatched page: every button in the card gets
            // top margin (separates the login button from the text above, and the consent
            // approve/deny buttons from each other).
            . '.card-body .btn{display:block;width:100%;margin-top:.75rem}'
            . 'code{word-break:break-all}';

        return '<!doctype html><html lang="' . $htmlLang . '"><head><meta charset="utf-8">'
            . '<meta name="viewport" content="width=device-width,initial-scale=1">'
            . '<meta name="robots" content="noindex,nofollow">'
            . '<title>' . $safeTitle . '</title>'
            . $styleTags
            . '<style>' . $fallbackCss . '</style>'
            . '</head><body>'
            . '<div class="vertical-center"><div class="container"><div id="login_wrapper">'
            . '<div id="login_outer" class="card"><div class="card-body">'
            . '<p class="text-center mb-2">' . $logoHtml . '</p>'
            . '<h1 class="consent-heading">' . $headingText . '</h1>'
            . $body
            . '</div></div></div></div></div>'
            . '</body></html>';
    }
}
