<?php

declare(strict_types=1);

namespace PpPhp\Core\Build;

/**
 * Compiles a Designer common Change Password page (a `password_form`
 * component) into the generated change-password.php. Renders inside the
 * application shell; the password policy (min length, character classes)
 * drives BOTH the live client checklist and the server-side validation,
 * so the two can never drift apart.
 */
final class ChangePasswordPageCompiler
{
    public static function supports(array $page): bool
    {
        return self::formProps($page) !== [];
    }

    public static function compile(array $page): string
    {
        $props = self::formProps($page);
        if ($props === []) {
            throw new \InvalidArgumentException('The Change Password page does not contain a password_form component.');
        }

        $tpl = in_array($props['template'] ?? '', ['coach', 'compact', 'verdant'], true) ? $props['template'] : 'guard';
        $accent = self::color((string) ($props['accentColor'] ?? '#2563eb'), '#2563eb');
        $minLen = max(4, (int) ($props['minLength'] ?? 8));
        $needUpper = !empty($props['requireUpper']);
        $needNumber = !empty($props['requireNumber']);
        $needSymbol = !empty($props['requireSymbol']);

        // Server-side policy checks mirroring the checklist
        $policyPhp = "    } elseif (strlen(\$new) < {$minLen}) {\n        \$error = 'New password must be at least {$minLen} characters.';\n";
        if ($needUpper) {
            $policyPhp .= "    } elseif (!preg_match('/[A-Z]/', \$new)) {\n        \$error = 'New password must contain an uppercase letter.';\n";
        }
        if ($needNumber) {
            $policyPhp .= "    } elseif (!preg_match('/[0-9]/', \$new)) {\n        \$error = 'New password must contain a number.';\n";
        }
        if ($needSymbol) {
            $policyPhp .= "    } elseif (!preg_match('/[^a-zA-Z0-9]/', \$new)) {\n        \$error = 'New password must contain a symbol.';\n";
        }

        $field = static function (string $label, string $name, string $icon, bool $eye = false): string {
            return '<label class="pp-pwd-field"><span>' . self::e($label) . '</span><div><i class="bi ' . $icon . '"></i>'
                . '<input type="password" name="' . $name . '" autocomplete="' . ($name === 'current_password' ? 'current-password' : 'new-password') . '" required>'
                . ($eye ? '<button type="button" class="pp-pwd-eye" data-eye tabindex="-1" aria-label="Show password"><i class="bi bi-eye"></i></button>' : '')
                . '</div></label>';
        };

        $reqItems = ['len' => 'At least ' . $minLen . ' characters'];
        if ($needUpper) $reqItems['upper'] = 'One uppercase letter';
        if ($needNumber) $reqItems['num'] = 'One number';
        if ($needSymbol) $reqItems['sym'] = 'One symbol';
        $reqs = '';
        if (!empty($props['showRequirements'])) {
            $reqs = '<ul class="pp-pwd-reqs" data-reqs>';
            foreach ($reqItems as $key => $text) {
                $reqs .= '<li data-req="' . $key . '"><i class="bi bi-circle"></i>' . self::e($text) . '</li>';
            }
            $reqs .= '</ul>';
        }
        $strength = !empty($props['showStrength'])
            ? '<div class="pp-pwd-strength" data-strength><i></i><i></i><i></i><i></i><small>&nbsp;</small></div>'
            : '';
        $match = '<div class="pp-pwd-match" data-match hidden><i class="bi bi-x-circle"></i> Passwords do not match</div>';

        $form = '<form method="post" data-pwd-form>'
            . '<?= csrfField() ?>'
            . $field((string) ($props['currentLabel'] ?? 'Current password'), 'current_password', 'bi-lock')
            . $field((string) ($props['newLabel'] ?? 'New password'), 'password', 'bi-shield-lock', true)
            . $strength . $reqs
            . $field((string) ($props['confirmLabel'] ?? 'Confirm new password'), 'confirm_password', 'bi-check2-circle')
            . $match
            . '<button class="pp-pwd-save" type="submit">' . self::e((string) ($props['submitLabel'] ?? 'Update password')) . '</button>'
            . '<a class="pp-pwd-back" href="<?= APP_BASE ?>/generated/auth/profile.php">' . self::e((string) ($props['backLabel'] ?? 'Back to profile')) . '</a>'
            . '</form>';

        $head = '<div class="pp-pwd-head"><h2>' . self::e((string) ($props['title'] ?? 'Change password')) . '</h2>'
            . ((string) ($props['subtitle'] ?? '') !== '' ? '<p>' . self::e((string) $props['subtitle']) . '</p>' : '') . '</div>';
        $shield = '<span class="pp-pwd-shield"><i class="bi bi-shield-lock"></i></span>';
        $alerts = '<?php if ($error): ?><div class="pp-pwd-alert"><i class="bi bi-exclamation-circle"></i> <?= htmlspecialchars($error) ?></div><?php endif; ?><?= flashMessage() ?>';

        $body = match ($tpl) {
            'coach' => '<section class="pp-pwdform is-coach" style="--pwd-accent:var(--pp-primary,' . $accent . ')">'
                . '<aside class="pp-pwd-tips">' . $shield . '<strong>' . self::e((string) ($props['tipsTitle'] ?? '')) . '</strong><p>'
                . self::e((string) ($props['tipsText'] ?? '')) . '</p></aside>'
                . '<div class="pp-pwd-main">' . $head . $alerts . $form . '</div></section>',
            'compact' => '<section class="pp-pwdform is-compact" style="--pwd-accent:var(--pp-primary,' . $accent . ')"><div class="pp-pwd-card">'
                . $head . $alerts . $form . '</div></section>',
            'verdant' => '<section class="pp-pwdform is-verdant" style="--pwd-accent:var(--pp-primary,' . $accent . ')"><div class="pp-pwd-card">'
                . $shield . $head . $alerts . $form . '</div></section>',
            default => '<section class="pp-pwdform is-guard" style="--pwd-accent:var(--pp-primary,' . $accent . ')"><div class="pp-pwd-card">'
                . $shield . $head . $alerts . $form . '</div></section>',
        };

        return strtr(self::documentShell(), [
            '__TITLE__' => self::e((string) ($props['title'] ?? 'Change password')),
            '__POLICY__' => rtrim($policyPhp),
            '__STYLES__' => self::styles(),
            '__BODY__' => $body,
            '__JS__' => self::script($minLen, $needUpper, $needNumber, $needSymbol),
        ]);
    }

    private static function documentShell(): string
    {
        return <<<'PHP'
<?php
require_once __DIR__ . '/../../_runtime.php';
require_once __DIR__ . '/../../app/shell.php';
require_once __DIR__ . '/../../app/lang.php';
\PpPhp\Core\Security::RequireAuth();
$user = \PpPhp\Core\Security::CurrentUserData();
$error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $current = (string) ($_POST['current_password'] ?? '');
    $new = (string) ($_POST['password'] ?? '');
    $confirm = (string) ($_POST['confirm_password'] ?? '');
    $row = \PpPhp\Core\DB::FetchAll('SELECT password FROM users WHERE id = ?', [$user['id']])[0] ?? null;
    if (!$row || !password_verify($current, (string) $row['password'])) {
        $error = 'Your current password is incorrect.';
__POLICY__
    } elseif ($new !== $confirm) {
        $error = 'New passwords do not match.';
    } else {
        \PpPhp\Core\DB::Update('users', ['password' => \PpPhp\Core\Security::HashPassword($new)], ['id' => $user['id']]);
        \PpPhp\Core\Events::fire('global.auth.afterPasswordChange', ['user' => $user]);
        $_SESSION['_flash'] = ['message' => 'Password changed successfully.', 'type' => 'success'];
        header('Location: ' . APP_BASE . '/generated/auth/profile.php');
        exit;
    }
}
?><!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>__TITLE__</title>
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/icons/bootstrap-icons.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/css/designer-base.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/vendor/inter/inter.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/vendor/fonts/fonts.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/vendor/fontawesome/7.2/css/fontawesome.min.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/vendor/fontawesome/7.2/css/solid.min.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/vendor/fontawesome/7.2/css/regular.min.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/vendor/material-symbols/material-symbols.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/css/theme.css">
<link rel="stylesheet" href="<?= APP_BASE ?>/assets/css/shell.css">
<style>__STYLES__</style>
</head>
<body>
<?php pp_shell_start('__TITLE__', ''); ?>
__BODY__
<?php pp_shell_end(); ?>
<script>__JS__</script>
</body>
</html>
PHP;
    }

    /** Live strength meter + requirements checklist + match + reveal toggles. */
    private static function script(int $minLen, bool $upper, bool $number, bool $symbol): string
    {
        $flags = json_encode(['min' => $minLen, 'upper' => $upper, 'num' => $number, 'sym' => $symbol]);
        return <<<JS
(function () {
    var cfg = $flags;
    var form = document.querySelector('[data-pwd-form]');
    if (!form) return;
    var pwd = form.querySelector('input[name="password"]');
    var confirm = form.querySelector('input[name="confirm_password"]');
    var bars = form.querySelectorAll('[data-strength] i');
    var label = form.querySelector('[data-strength] small');
    var names = ['Weak', 'Fair', 'Good', 'Strong'];
    function score(v) {
        var s = 0;
        if (v.length >= cfg.min) s++;
        if (v.length >= cfg.min + 4) s++;
        if (/[a-z]/.test(v) && /[A-Z]/.test(v)) s++;
        if (/[0-9]/.test(v) && /[^a-zA-Z0-9]/.test(v)) s++;
        else if (/[0-9]/.test(v) || /[^a-zA-Z0-9]/.test(v)) s += (s < 3 ? 1 : 0);
        return Math.min(4, Math.max(v ? 1 : 0, s));
    }
    function update() {
        var v = pwd.value;
        var s = score(v);
        bars.forEach(function (b, i) { b.className = i < s ? 'on s' + s : ''; });
        if (label) label.textContent = v ? names[s - 1] || 'Weak' : '\\u00a0';
        var checks = { len: v.length >= cfg.min, upper: !cfg.upper || /[A-Z]/.test(v), num: !cfg.num || /[0-9]/.test(v), sym: !cfg.sym || /[^a-zA-Z0-9]/.test(v) };
        form.querySelectorAll('[data-req]').forEach(function (li) {
            var ok = checks[li.getAttribute('data-req')];
            li.classList.toggle('ok', !!ok && !!v);
            li.querySelector('i').className = 'bi ' + (ok && v ? 'bi-check-circle-fill' : 'bi-circle');
        });
        matchCheck();
    }
    function matchCheck() {
        var m = form.querySelector('[data-match]');
        if (!m) return;
        m.hidden = !confirm.value || confirm.value === pwd.value;
    }
    if (pwd) pwd.addEventListener('input', update);
    if (confirm) confirm.addEventListener('input', matchCheck);
    form.querySelectorAll('[data-eye]').forEach(function (btn) {
        btn.addEventListener('click', function () {
            var input = btn.parentElement.querySelector('input');
            var show = input.type === 'password';
            input.type = show ? 'text' : 'password';
            btn.querySelector('i').className = 'bi ' + (show ? 'bi-eye-slash' : 'bi-eye');
        });
    });
})();
JS;
    }

    private static function styles(): string
    {
        return <<<'CSS'
.pp-pwdform{color:var(--pp-text,#0f172a)}
.pp-pwd-shield{width:52px;height:52px;border-radius:14px;display:grid;place-items:center;font-size:22px;background:color-mix(in srgb,var(--pwd-accent) 12%,var(--pp-surface,#fff));color:var(--pwd-accent);margin-bottom:10px}
.pp-pwd-head h2{margin:0 0 3px;font-size:20px;letter-spacing:-.02em}
.pp-pwd-head p{margin:0 0 16px;color:var(--pp-muted,#64748b);font-size:12.5px}
.pp-pwd-alert{margin:0 0 12px;padding:9px 12px;border-radius:10px;font-size:12px;border:1px solid #fecaca;background:#fef2f2;color:#991b1b;display:flex;align-items:center;gap:7px}
.pp-pwd-field{display:grid;gap:5px;font-size:11.5px;font-weight:700;margin-bottom:11px}
.pp-pwd-field>div{position:relative;display:flex;align-items:center}
.pp-pwd-field>div>i:first-child{position:absolute;left:11px;color:var(--pp-muted,#94a3b8);font-size:13px;pointer-events:none}
.pp-pwd-field input{width:100%;height:42px;border:1px solid var(--pp-border,#dfe3ec);border-radius:10px;padding:0 38px;font:inherit;background:var(--pp-surface,#fff);color:var(--pp-text,#0f172a);outline:none}
.pp-pwd-field input:focus{border-color:var(--pwd-accent);box-shadow:0 0 0 3px color-mix(in srgb,var(--pwd-accent) 15%,transparent)}
.pp-pwd-eye{position:absolute;right:6px;border:0;background:transparent;color:var(--pp-muted,#94a3b8);cursor:pointer;padding:6px}
.pp-pwd-strength{display:flex;align-items:center;gap:5px;margin:-3px 0 10px}
.pp-pwd-strength i{flex:1;height:5px;border-radius:99px;background:var(--pp-border,#e2e8f0);transition:background .2s}
.pp-pwd-strength i.on.s1{background:#dc2626}.pp-pwd-strength i.on.s2{background:#d97706}.pp-pwd-strength i.on.s3{background:#65a30d}.pp-pwd-strength i.on.s4{background:#16a34a}
.pp-pwd-strength small{color:var(--pp-muted,#64748b);font-size:10.5px;font-weight:700;min-width:44px;text-align:right}
.pp-pwd-reqs{list-style:none;padding:0;margin:0 0 12px;display:grid;gap:4px}
.pp-pwd-reqs li{display:flex;align-items:center;gap:7px;font-size:11.5px;color:var(--pp-muted,#64748b);transition:color .15s}
.pp-pwd-reqs li.ok{color:#16a34a}
.pp-pwd-match{margin:-4px 0 10px;font-size:11.5px;color:#dc2626;display:flex;align-items:center;gap:6px}
.pp-pwd-save{display:block;width:100%;border:0;border-radius:10px;height:42px;background:var(--pwd-accent);color:#fff;font-weight:750;cursor:pointer}
.pp-pwd-save:hover{filter:brightness(1.06)}
.pp-pwd-back{display:block;margin-top:12px;text-align:center;color:var(--pwd-accent);font-size:12px;font-weight:700;text-decoration:none}
.pp-pwdform.is-guard{display:grid;place-items:start center;padding:10px 0}
.pp-pwdform.is-guard .pp-pwd-card{width:min(100%,440px);background:var(--pp-surface,#fff);border:1px solid var(--pp-border,#e2e8f0);border-radius:18px;padding:28px;box-shadow:var(--pp-shadow,0 14px 40px rgba(15,23,42,.07))}
.pp-pwdform.is-coach{display:grid;grid-template-columns:250px minmax(0,560px);gap:16px;align-items:start}
.pp-pwd-tips{background:color-mix(in srgb,var(--pwd-accent) 6%,var(--pp-surface,#fff));border:1px solid color-mix(in srgb,var(--pwd-accent) 18%,transparent);border-radius:16px;padding:22px}
.pp-pwd-tips strong{display:block;margin-bottom:6px;font-size:14px}
.pp-pwd-tips p{margin:0;color:var(--pp-muted,#64748b);font-size:12px;line-height:1.6}
.pp-pwdform.is-coach .pp-pwd-main{background:var(--pp-surface,#fff);border:1px solid var(--pp-border,#e2e8f0);border-radius:16px;padding:24px 26px}
.pp-pwdform.is-compact .pp-pwd-card{max-width:480px;background:var(--pp-surface,#fff);border:1px solid var(--pp-border,#e2e8f0);border-radius:14px;padding:22px}
.pp-pwdform.is-verdant{position:relative;isolation:isolate;overflow:hidden;display:grid;place-items:center;min-height:clamp(540px,72vh,760px);padding:clamp(22px,5vw,64px);border-radius:22px;color:#fff;background:radial-gradient(ellipse at 72% 103%,#77ad26 0 18%,transparent 18.4%),radial-gradient(ellipse at 25% 110%,#91bf31 0 26%,transparent 26.4%),linear-gradient(160deg,transparent 52%,#3b731f 52.3% 67%,#244c22 67.4% 100%),linear-gradient(180deg,#a9ced2 0%,#eed49f 43%,#79945f 64%,#284c2a 100%)}
.pp-pwdform.is-verdant:before{content:"";position:absolute;inset:0;z-index:-1;opacity:.55;background:radial-gradient(ellipse at 6% 60%,rgba(22,51,48,.8) 0 10%,transparent 10.5%)}
.pp-pwdform.is-verdant .pp-pwd-card{width:min(100%,500px);padding:clamp(24px,4vw,42px);border:1px solid rgba(255,255,255,.58);border-radius:28px;background:linear-gradient(145deg,rgba(255,255,255,.25),rgba(11,38,28,.34));box-shadow:0 28px 70px rgba(8,25,18,.32),inset 0 1px rgba(255,255,255,.5);-webkit-backdrop-filter:blur(22px) saturate(125%);backdrop-filter:blur(22px) saturate(125%)}
.pp-pwdform.is-verdant .pp-pwd-head p,.pp-pwdform.is-verdant .pp-pwd-reqs li,.pp-pwdform.is-verdant .pp-pwd-strength small{color:rgba(255,255,255,.76)}
.pp-pwdform.is-verdant .pp-pwd-field input{color:#fff;background:rgba(7,31,24,.23);border-color:rgba(255,255,255,.42)}
.pp-pwdform.is-verdant .pp-pwd-field input::placeholder{color:rgba(255,255,255,.62)}
.pp-pwdform.is-verdant .pp-pwd-field>div>i:first-child,.pp-pwdform.is-verdant .pp-pwd-eye{color:rgba(255,255,255,.72)}
.pp-pwdform.is-verdant .pp-pwd-save{background:linear-gradient(100deg,#c2d508,#27bf68);box-shadow:0 10px 26px rgba(42,178,82,.28)}
.pp-pwdform.is-verdant .pp-pwd-back{color:#eaffae}
@media(max-width:720px){.pp-pwdform.is-coach{grid-template-columns:1fr}.pp-pwdform.is-verdant{min-height:560px;padding:18px;border-radius:16px}.pp-pwdform.is-verdant .pp-pwd-card{padding:24px 20px;border-radius:22px}}
CSS;
    }

    private static function formProps(array $page): array
    {
        $walk = function (array $components) use (&$walk): array {
            foreach ($components as $component) {
                if (!is_array($component)) {
                    continue;
                }
                if (($component['type'] ?? '') === 'password_form') {
                    return (array) ($component['props'] ?? []);
                }
                $found = $walk((array) ($component['children'] ?? []));
                if ($found !== []) {
                    return $found;
                }
            }
            return [];
        };

        return $walk((array) ($page['components'] ?? []));
    }

    private static function color(string $value, string $fallback): string
    {
        return preg_match('/^#[0-9a-f]{3,8}$/i', $value) ? $value : $fallback;
    }

    private static function e(string $value): string
    {
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
    }
}
