<?php
include('includes/auth.php');

$page = 'security.php';

$passkeyError = '';
$passkeySuccess = '';

if (isset($_POST['delete_passkey'])) {
    if (!verify_token($_POST['csrf_token'] ?? '')) {
        $passkeyError = 'Invalid request. Please try again.';
    } else {
        $credentialIdToDelete = (int) ($_POST['credential_id'] ?? 0);
        if ($credentialIdToDelete > 0) {
            $db->delete('webauthn_credentials', 'id = :id AND user_id = :uid', [':id' => $credentialIdToDelete, ':uid' => 1]);
            $passkeySuccess = 'Passkey removed.';
        }
    }
}

if (isset($_POST['rename_passkey'])) {
    if (!verify_token($_POST['csrf_token'] ?? '')) {
        $passkeyError = 'Invalid request. Please try again.';
    } else {
        $credentialIdToRename = (int) ($_POST['credential_id'] ?? 0);
        $newLabel = trim((string) ($_POST['label'] ?? ''));
        if ($credentialIdToRename > 0 && $newLabel !== '') {
            $db->update(
                'webauthn_credentials',
                ['label' => $newLabel],
                'id = :id AND user_id = :uid',
                [':id' => $credentialIdToRename, ':uid' => 1]
            );
            $passkeySuccess = 'Passkey renamed.';
        }
    }
}

$passkeys = $db->select('webauthn_credentials', '*', 'user_id = :uid', 'created_at DESC', [':uid' => 1]);

include('includes/header.php');

$csrfToken = generate_token();
?>

<div class="md-card md-card--elevated" style="max-width: 700px; margin: 0 auto;">
    <div class="md-card__header">
        <?= icon('fingerprint', 'md-card__header-icon') ?>
        <h2 class="md-card__title">Passkeys (Two-Factor Login)</h2>
    </div>
    <div class="md-card__content">
        <p class="md-helper-text">
            Add a passkey (Windows Hello, Touch ID/Face ID, a USB security key, or a phone) to require it
            alongside your password when signing in. If you have no passkeys registered, login works with
            just your password as it does today.
        </p>

        <?php if ($passkeyError): ?>
        <div class="md-alert md-alert--error">
            <?= icon('error') ?>
            <span><?= htmlspecialchars($passkeyError) ?></span>
        </div>
        <?php endif; ?>

        <?php if ($passkeySuccess): ?>
        <div class="md-alert md-alert--success">
            <?= icon('check_circle') ?>
            <span><?= htmlspecialchars($passkeySuccess) ?></span>
        </div>
        <?php endif; ?>

        <div id="registerError" class="md-alert md-alert--error" style="display: none;">
            <?= icon('error') ?>
            <span id="registerErrorText"></span>
        </div>

        <div class="md-card__actions" style="padding-left: 0; padding-right: 0;">
            <button type="button" id="addPasskeyBtn" class="md-button md-button--filled">
                <?= icon('add') ?>
                <span class="md-button__label">Add a Passkey</span>
            </button>
        </div>

        <?php if (empty($passkeys)): ?>
        <div class="md-empty-state">
            <?= icon('fingerprint', 'md-empty-state__icon') ?>
            <p class="md-empty-state__text">No passkeys registered yet. Login uses your password only.</p>
        </div>
        <?php else: ?>
        <div class="md-table-container" style="margin-top: 1rem;">
            <table class="md-table">
                <thead>
                    <tr>
                        <th>Label</th>
                        <th>Added</th>
                        <th>Last Used</th>
                        <th></th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($passkeys as $passkey): ?>
                    <tr>
                        <td>
                            <form method="post" style="display: flex; align-items: center; gap: 0.5rem;">
                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
                                <input type="hidden" name="credential_id" value="<?= (int) $passkey['id'] ?>">
                                <input type="text" name="label" value="<?= htmlspecialchars($passkey['label'] ?? '') ?>"
                                       class="md-text-field__input" style="border: none; background: transparent; padding: 0.25rem; width: 160px;">
                                <button type="submit" name="rename_passkey" class="md-icon-button" title="Save name" aria-label="Save name">
                                    <span style="font-size: 18px;"><?= icon('save') ?></span>
                                </button>
                            </form>
                        </td>
                        <td><?= htmlspecialchars($passkey['created_at'] ?? '—') ?></td>
                        <td><?= htmlspecialchars($passkey['last_used_at'] ?? 'Never') ?></td>
                        <td>
                            <form method="post" onsubmit="return confirm('Remove this passkey? You will no longer be able to use it to sign in.');">
                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
                                <input type="hidden" name="credential_id" value="<?= (int) $passkey['id'] ?>">
                                <button type="submit" name="delete_passkey" class="md-button md-button--text md-button--error">
                                    <?= icon('delete') ?>
                                    <span class="md-button__label">Remove</span>
                                </button>
                            </form>
                        </td>
                    </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <?php endif; ?>
    </div>
</div>

<script>
    (function () {
        const csrfToken = <?= json_encode($csrfToken) ?>;

        function base64ToArrayBuffer(b64) {
            const binary = window.atob(b64);
            const bytes = new Uint8Array(binary.length);
            for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
            return bytes.buffer;
        }
        function arrayBufferToBase64(buffer) {
            const bytes = new Uint8Array(buffer);
            let binary = '';
            for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
            return window.btoa(binary);
        }
        // Unwraps this library's RFC1342-like "=?BINARY?B?...?=" wrapper
        // (see vendor/lbuchs/webauthn's own _test/client.html) into
        // ArrayBuffers, recursively.
        function unwrapBinaryFields(obj) {
            const prefix = '=?BINARY?B?', suffix = '?=';
            if (obj !== null && typeof obj === 'object') {
                for (const key in obj) {
                    if (typeof obj[key] === 'string') {
                        const str = obj[key];
                        if (str.startsWith(prefix) && str.endsWith(suffix)) {
                            obj[key] = base64ToArrayBuffer(str.substring(prefix.length, str.length - suffix.length));
                        }
                    } else {
                        unwrapBinaryFields(obj[key]);
                    }
                }
            }
        }

        function showRegisterError(message) {
            const box = document.getElementById('registerError');
            document.getElementById('registerErrorText').textContent = message;
            box.style.display = 'flex';
        }

        document.getElementById('addPasskeyBtn')?.addEventListener('click', async function () {
            if (!window.fetch || !navigator.credentials || !navigator.credentials.create) {
                showRegisterError('This browser does not support passkeys.');
                return;
            }
            document.getElementById('registerError').style.display = 'none';

            const label = window.prompt('Name this passkey (e.g. "My Laptop", "YubiKey"):', '');
            if (label === null) {
                return; // user cancelled the prompt
            }

            try {
                const optRes = await fetch('api/webauthn-register-options.php?csrf_token=' + encodeURIComponent(csrfToken));
                const optData = await optRes.json();
                if (!optData.success) {
                    showRegisterError(optData.error || 'Unable to start passkey registration.');
                    return;
                }
                unwrapBinaryFields(optData.options);

                const credential = await navigator.credentials.create(optData.options);

                const verifyBody = {
                    csrf_token: csrfToken,
                    label: label,
                    clientDataJSON: arrayBufferToBase64(credential.response.clientDataJSON),
                    attestationObject: arrayBufferToBase64(credential.response.attestationObject),
                };

                const verifyRes = await fetch('api/webauthn-register-verify.php', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(verifyBody),
                });
                const verifyData = await verifyRes.json();
                if (verifyData.success) {
                    window.location.reload();
                } else {
                    showRegisterError(verifyData.error || 'Passkey registration failed.');
                }
            } catch (err) {
                showRegisterError(err.message || 'Passkey registration was cancelled or failed.');
            }
        });
    })();
</script>

<?php include('includes/footer.php'); ?>
