<?php
/**
 * index.php — Secure login page with Material Design 3
 *
 * Security features:
 * - bcrypt password verification with legacy plaintext upgrade
 * - Account lockout after 5 failed attempts (15-minute cooldown)
 * - CSRF token protection
 * - Session fixation prevention via session_regenerate_id()
 * - Rate limiting via failed_attempts tracking
 */

include('includes/functions.php');

$table_name = 'user';

// Check if already logged in
$log_check = $db->select($table_name, '*', 'id = :id', '', [':id' => 1]);
$loggedinuser = !empty($log_check) ? $log_check[0]['username'] : null;

if (!empty($loggedinuser) && isset($_SESSION['name']) && $_SESSION['name'] === $loggedinuser) {
    header("Location: main.php");
    exit;
}

// Cancel a pending second-factor attempt and return to the password form
// (e.g. the user picked the wrong account, or their passkey isn't handy).
if (isset($_GET['cancel_2fa'])) {
    unset($_SESSION['pending_2fa_user_id'], $_SESSION['webauthn_login_challenge']);
    header('Location: index.php');
    exit;
}

$pending2faUserId = $_SESSION['pending_2fa_user_id'] ?? null;

$error = '';
$csrfToken = generate_token();

if (isset($_POST['login']) && $pending2faUserId === null) {
    // Verify CSRF token
    if (!isset($_POST['csrf_token']) || !verify_token($_POST['csrf_token'])) {
        $error = 'Invalid request. Please try again.';
    } elseif (login_throttle_blocked()) {
        $error = 'Too many failed attempts. Try again in a few minutes.';
    } else {
        $username = trim($_POST['username'] ?? '');
        $password = $_POST['password'] ?? '';

        $userData = $db->select($table_name, '*', 'username = :username', '', [':username' => $username]);

        if ($userData) {
            $user = $userData[0];

            // Check account lockout
            if (is_account_locked($user)) {
                $lockRemaining = max(0, strtotime($user['locked_until']) - time());
                $minutes = ceil($lockRemaining / 60);
                $error = "Account locked. Try again in {$minutes} minute(s).";
            } elseif (verify_password($password, $user['password'])) {
                // Successful password check
                reset_failed_login($db, (int) $user['id']);
                login_throttle_clear();

                // Upgrade legacy plaintext password to bcrypt
                if (!str_starts_with($user['password'], '$2y$') && !str_starts_with($user['password'], '$2b$')) {
                    $newHash = hash_password($password);
                    $db->update($table_name, [
                        'password' => $newHash,
                        'password_changed_at' => date('Y-m-d H:i:s'),
                    ], 'id = :id', [':id' => $user['id']]);
                } elseif (password_needs_rehash_check($user['password'])) {
                    // Rehash if cost has changed
                    $newHash = hash_password($password);
                    $db->update($table_name, [
                        'password' => $newHash,
                    ], 'id = :id', [':id' => $user['id']]);
                }

                // If this account has registered passkeys, require that
                // second factor before completing login — the password
                // alone is not sufficient. See api/webauthn-login-*.php.
                $hasPasskeys = $db->count('webauthn_credentials', 'user_id = :uid', [':uid' => (int) $user['id']]) > 0;

                if ($hasPasskeys) {
                    session_regenerate_id(true);
                    $_SESSION['pending_2fa_user_id'] = (int) $user['id'];
                    header('Location: index.php');
                    exit;
                }

                session_regenerate_id(true);
                $_SESSION['loggedin'] = true;
                $_SESSION['name'] = $user['username'];
                $_SESSION['login_time'] = time();

                header('Location: main.php');
                exit;
            } else {
                // Failed login
                record_failed_login($db, (int) $user['id'], (int) ($user['failed_attempts'] ?? 0));
                login_throttle_record();
                $error = 'Invalid username or password.';
            }
        } else {
            // User not found — use generic error to prevent enumeration
            login_throttle_record();
            $error = 'Invalid username or password.';
        }
    }
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Login — <?= htmlspecialchars($config_ini['titles']['panel_name'] ?? 'IPTV Panel') ?></title>
    <link rel="icon" type="image/x-icon" href="./img/favicon.ico">
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
    <link href="css/material.css" rel="stylesheet">
    <link href="css/login.css" rel="stylesheet">
</head>
<body class="md-login-body">
    <div class="md-login-container">
        <div class="md-login-card">
            <div class="md-login-card__header">
                <img src="./img/login_logo.png" alt="Logo" class="md-login-card__logo">
                <h1 class="md-login-card__title"><?= htmlspecialchars($config_ini['titles']['panel_name'] ?? 'IPTV Panel') ?></h1>
                <p class="md-login-card__subtitle">Sign in to your account</p>
            </div>

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

            <?php if ($pending2faUserId !== null): ?>
            <div class="md-login-card__form" id="passkeyStep">
                <p style="text-align: center; color: var(--md-on-surface-variant); margin-bottom: 1.5rem;">
                    Confirm it's you with your passkey.
                </p>
                <div id="passkeyError" class="md-alert md-alert--error" style="display: none;">
                    <?= icon('error') ?>
                    <span id="passkeyErrorText"></span>
                </div>
                <button type="button" id="passkeyVerifyBtn" class="md-button md-button--filled md-button--full-width">
                    <?= icon('fingerprint') ?>
                    <span class="md-button__label">Use Passkey</span>
                </button>
                <a href="index.php?cancel_2fa=1" class="md-link" style="display: block; text-align: center; margin-top: 1rem;">
                    Cancel and start over
                </a>
            </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 showPasskeyError(message) {
                        const box = document.getElementById('passkeyError');
                        document.getElementById('passkeyErrorText').textContent = message;
                        box.style.display = 'flex';
                    }

                    document.getElementById('passkeyVerifyBtn')?.addEventListener('click', async function () {
                        if (!window.fetch || !navigator.credentials || !navigator.credentials.get) {
                            showPasskeyError('This browser does not support passkeys.');
                            return;
                        }
                        try {
                            const optRes = await fetch('api/webauthn-login-options.php?csrf_token=' + encodeURIComponent(csrfToken));
                            const optData = await optRes.json();
                            if (!optData.success) {
                                showPasskeyError(optData.error || 'Unable to start passkey login.');
                                return;
                            }
                            unwrapBinaryFields(optData.options);

                            const assertion = await navigator.credentials.get(optData.options);

                            const verifyBody = {
                                csrf_token: csrfToken,
                                id: arrayBufferToBase64(assertion.rawId),
                                clientDataJSON: arrayBufferToBase64(assertion.response.clientDataJSON),
                                authenticatorData: arrayBufferToBase64(assertion.response.authenticatorData),
                                signature: arrayBufferToBase64(assertion.response.signature),
                            };

                            const verifyRes = await fetch('api/webauthn-login-verify.php', {
                                method: 'POST',
                                headers: { 'Content-Type': 'application/json' },
                                body: JSON.stringify(verifyBody),
                            });
                            const verifyData = await verifyRes.json();
                            if (verifyData.success) {
                                window.location.href = verifyData.redirect || 'main.php';
                            } else {
                                showPasskeyError(verifyData.error || 'Passkey verification failed.');
                            }
                        } catch (err) {
                            showPasskeyError(err.message || 'Passkey login was cancelled or failed.');
                        }
                    });
                })();
            </script>
            <?php else: ?>
            <form method="post" class="md-login-card__form" autocomplete="off">
                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">

                <div class="md-text-field">
                    <?= icon('person', 'md-text-field__icon') ?>
                    <input type="text" name="username" id="username" class="md-text-field__input" placeholder=" " required autocomplete="username">
                    <label for="username" class="md-text-field__label">Username</label>
                    <div class="md-text-field__indicator"></div>
                </div>

                <div class="md-text-field">
                    <?= icon('lock', 'md-text-field__icon') ?>
                    <input type="password" name="password" id="password" class="md-text-field__input" placeholder=" " required autocomplete="current-password">
                    <label for="password" class="md-text-field__label">Password</label>
                    <div class="md-text-field__indicator"></div>
                    <button type="button" class="md-icon-button md-text-field__toggle" id="togglePassword" aria-label="Toggle password visibility">
                        <?= icon('visibility') ?>
                    </button>
                </div>

                <button type="submit" name="login" class="md-button md-button--filled md-button--full-width">
                    <?= icon('login') ?>
                    <span class="md-button__label">Sign In</span>
                </button>
            </form>
            <?php endif; ?>

            <div class="md-login-card__footer">
                <a href="<?= htmlspecialchars($config_ini['titles']['contact'] ?? '#') ?>" target="_blank" class="md-link">
                    <?= icon('support_agent') ?>
                    Contact Support
                </a>
                <div class="app-footer">
                    <span>Made with <span class="app-footer__heart" aria-label="love">&hearts;</span> by <a href="https://krankhaus.ca" target="_blank" rel="noopener noreferrer">Krankhaus</a></span>
                </div>
            </div>
        </div>
    </div>


    <script>
        // Password visibility toggle
        document.getElementById('togglePassword')?.addEventListener('click', function() {
            const input = document.getElementById('password');
            const path = this.querySelector('svg path');
            if (input.type === 'password') {
                input.type = 'text';
                path.setAttribute('d', 'M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2.41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6zm-1.07 1.14L13.9 10.1c.05-.03.1-.06.1-.1 0-1.1-.9-2-2-2-.04 0-.07.05-.1.1zM2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45 2.01 3.87zm7.5 7.5l2.61 2.61c-.04.01-.08.02-.12.02-1.38 0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13zm-3.4-3.4l1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 4.5 4.5 4.5.63 0 1.23-.13 1.78-.36l.98.98c-.88.24-1.8.38-2.76.38-3.79 0-7.17-2.13-8.82-5.5.59-1.22 1.42-2.27 2.41-3.12z');
            } else {
                input.type = 'password';
                path.setAttribute('d', 'M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z');
            }
        });
    </script>
</body>
</html>
