<?php
ob_start();
require_once("../config/session.php");
require_once("../config/function.php");

$site_data = site_data();
$main_site_data = main_site_data();

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

if (empty($_SESSION['auth_csrf'])) {
    $_SESSION['auth_csrf'] = bin2hex(random_bytes(32));
}


function auth_member_identifier($identifier) {
    $identifier = trim((string)$identifier);
    $mobile = auth_mobile_value($identifier);

    if ($mobile !== '') {
        $safeMobile = real_escape_string($mobile);
        $safeUsername = real_escape_string(get_username($mobile));
        $rows = db_fetch_all(
            'members',
            "mobile='".$safeMobile."' OR username='".$safeUsername."'",
            'user_id,username,name,email,mobile,password,status'
        );
        return is_array($rows) && count($rows) > 0 ? $rows[0] : null;
    }

    $email = auth_email_value($identifier);
    if ($email !== '') {
        $safeEmail = real_escape_string($email);
        $rows = db_fetch_all(
            'members',
            "email='".$safeEmail."'",
            'user_id,username,name,email,mobile,password,status'
        );
        return is_array($rows) && count($rows) > 0 ? $rows[0] : null;
    }

    return null;
}


function auth_site_value($siteData, $wantedKey, $default = '') {
    if (!is_array($siteData)) {
        return $default;
    }

    foreach ($siteData as $key => $value) {
        if (strcasecmp((string)$key, (string)$wantedKey) === 0) {
            return $value;
        }
    }

    return $default;
}

function auth_new_sms_enabled($siteData) {
    return strcasecmp(trim((string)auth_site_value($siteData, 'newSmsService', 'No')), 'Yes') === 0;
}

function auth_member_login_identifier($identifier) {
    $member = auth_member_identifier($identifier);
    if ($member) {
        return $member;
    }

    $identifier = trim((string)$identifier);
    if ($identifier === '') {
        return null;
    }

    $safeIdentifier = real_escape_string($identifier);
    $rows = db_fetch_all(
        'members',
        "username='".$safeIdentifier."'",
        'user_id,username,name,email,mobile,password,status'
    );

    return is_array($rows) && count($rows) > 0 ? $rows[0] : null;
}

function auth_new_sms_template($siteData) {
    $stored = trim((string)auth_site_value($siteData, 'NewUrlsms', ''));
    if ($stored === '') {
        return '';
    }

    $decoded = json_decode($stored, true);
    if (is_array($decoded) && !empty($decoded['url'])) {
        return trim((string)$decoded['url']);
    }

    return $stored;
}

function auth_build_new_sms_url($siteData, $action, $mobile, $extra = array()) {
    $template = auth_new_sms_template($siteData);
    $mobile = auth_mobile_value($mobile);

    if ($template === '' || $mobile === '') {
        return '';
    }

    $template = str_ireplace(
        array('{NUMBER}', '{MOBILE}', '{{NUMBER}}', '{{MOBILE}}'),
        rawurlencode($mobile),
        $template
    );

    $parts = parse_url($template);
    if (!is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
        return '';
    }

    $scheme = strtolower((string)$parts['scheme']);
    if (!in_array($scheme, array('https', 'http'), true)) {
        return '';
    }

    $query = array();
    if (!empty($parts['query'])) {
        parse_str((string)$parts['query'], $query);
    }

    $query['action'] = $action;
    $query['mobile'] = $mobile;
    foreach ($extra as $key => $value) {
        $query[$key] = (string)$value;
    }

    $auth = '';
    if (!empty($parts['user'])) {
        $auth = rawurlencode((string)$parts['user']);
        if (isset($parts['pass'])) {
            $auth .= ':' . rawurlencode((string)$parts['pass']);
        }
        $auth .= '@';
    }

    $port = isset($parts['port']) ? ':' . (int)$parts['port'] : '';
    $path = $parts['path'] ?? '/';

    return $scheme . '://' . $auth . $parts['host'] . $port . $path . '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);
}

function auth_http_json_get($url) {
    if ($url === '') {
        return array(false, array(), 'New SMS API URL is not configured.', 0);
    }

    if (function_exists('curl_init')) {
        $ch = curl_init($url);
        curl_setopt_array($ch, array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_CONNECTTIMEOUT => 8,
            CURLOPT_TIMEOUT => 20,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_HTTPHEADER => array('Accept: application/json'),
            CURLOPT_USERAGENT => 'RapidX-Auth-OTP/1.0'
        ));

        $raw = curl_exec($ch);
        $curlError = curl_error($ch);
        $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($raw === false) {
            error_log('NEW SMS API CURL ERROR: ' . $curlError);
            return array(false, array(), 'Unable to connect to OTP service.', $httpCode);
        }
    } else {
        $context = stream_context_create(array(
            'http' => array(
                'method' => 'GET',
                'timeout' => 20,
                'ignore_errors' => true,
                'header' => "Accept: application/json\r\nUser-Agent: RapidX-Auth-OTP/1.0\r\n"
            )
        ));
        $raw = @file_get_contents($url, false, $context);
        $httpCode = 0;
        if ($raw === false) {
            return array(false, array(), 'Unable to connect to OTP service.', 0);
        }
    }

    $decoded = json_decode((string)$raw, true);
    if (!is_array($decoded)) {
        error_log('NEW SMS API INVALID JSON. HTTP ' . $httpCode);
        return array(false, array(), 'OTP service returned an invalid response.', $httpCode);
    }

    return array(true, $decoded, '', $httpCode);
}

function auth_external_send_otp($siteData, $mobile) {
    $url = auth_build_new_sms_url($siteData, 'send_otp', $mobile);
    list($connected, $response, $transportMessage, $httpCode) = auth_http_json_get($url);

    if (!$connected) {
        return array('status' => false, 'message' => $transportMessage);
    }

    $success = strtolower(trim((string)($response['status'] ?? ''))) === 'success';
    $token = trim((string)($response['data']['otp_token'] ?? ''));
    if (!$success || $token === '') {
        $message = trim((string)($response['message'] ?? 'Unable to send OTP.'));
        error_log('NEW SMS SEND FAILED. HTTP ' . $httpCode . '; status=' . (string)($response['status'] ?? ''));
        return array('status' => false, 'message' => $message !== '' ? $message : 'Unable to send OTP.');
    }

    $expiresIn = (int)($response['data']['expires_in'] ?? 300);
    if ($expiresIn < 60 || $expiresIn > 1800) {
        $expiresIn = 300;
    }

    return array(
        'status' => true,
        'message' => (string)($response['message'] ?? 'OTP sent successfully.'),
        'otp_token' => $token,
        'expires_in' => $expiresIn,
        'masked_mobile' => (string)($response['data']['mobile'] ?? '')
    );
}

function auth_external_verify_otp($siteData, &$state, $submittedOtp) {
    $otp = preg_replace('/\D+/', '', (string)$submittedOtp);
    if (strlen($otp) !== 6) {
        return array(false, 'Please enter a valid 6 digit OTP.', '');
    }

    $provider = (string)($state['provider'] ?? '');
    $isMultiProvider = $provider === 'multi';
    $otpToken = $isMultiProvider
        ? (string)($state['new_sms_otp_token'] ?? '')
        : (string)($state['otp_token'] ?? '');
    $otpExpiry = $isMultiProvider
        ? (int)($state['new_sms_otp_expiry'] ?? 0)
        : (int)($state['otp_expiry'] ?? 0);
    $attemptKey = $isMultiProvider ? 'new_sms_otp_attempts' : 'otp_attempts';

    if (!in_array($provider, array('new_sms', 'multi'), true) || $otpToken === '' || empty($state['mobile'])) {
        return array(false, 'OTP session expired. Please request a new OTP.', '');
    }

    if ($otpExpiry < time()) {
        return array(false, 'OTP expired. Please request a new OTP.', '');
    }

    if ((int)($state[$attemptKey] ?? 0) >= 5) {
        return array(false, 'Too many invalid OTP attempts. Please restart.', '');
    }

    $url = auth_build_new_sms_url($siteData, 'verify_otp', $state['mobile'], array(
        'otp' => $otp,
        'otp_token' => $otpToken
    ));
    list($connected, $response, $transportMessage, $httpCode) = auth_http_json_get($url);

    if (!$connected) {
        return array(false, $transportMessage, '');
    }

    $success = strtolower(trim((string)($response['status'] ?? ''))) === 'success';
    $verified = !empty($response['data']['verified']);
    if (!$success || !$verified) {
        $state[$attemptKey] = (int)($state[$attemptKey] ?? 0) + 1;
        $message = trim((string)($response['message'] ?? 'Invalid OTP. Please try again.'));
        error_log('NEW SMS VERIFY FAILED. HTTP ' . $httpCode . '; status=' . (string)($response['status'] ?? ''));
        return array(false, $message !== '' ? $message : 'Invalid OTP. Please try again.', '');
    }

    $verifiedToken = trim((string)($response['data']['verified_token'] ?? ''));
    $state['verified'] = true;
    $state['verified_at'] = time();
    $state['verified_token'] = $verifiedToken;
    $state['otp_expiry'] = 0;
    $state['new_sms_otp_expiry'] = 0;
    unset($state['otp_token'], $state['new_sms_otp_token'], $state['otp_hash']);

    return array(true, (string)($response['message'] ?? 'OTP verified successfully.'), $verifiedToken);
}

function auth_external_state($baseState, $sendResult) {
    return array_merge($baseState, array(
        'provider' => 'new_sms',
        'stage' => 'otp',
        'otp_token' => (string)$sendResult['otp_token'],
        'otp_expiry' => time() + (int)$sendResult['expires_in'],
        'otp_attempts' => 0,
        'last_resend_at' => time()
    ));
}

function auth_multi_state($oldState, $sendResult) {
    $state = $oldState;
    $state['provider'] = 'multi';
    $state['stage'] = 'otp';
    $state['last_resend_at'] = time();
    $state['new_sms_available'] = !empty($sendResult['status']) && !empty($sendResult['otp_token']);

    if ($state['new_sms_available']) {
        $state['new_sms_otp_token'] = (string)$sendResult['otp_token'];
        $state['new_sms_otp_expiry'] = time() + (int)$sendResult['expires_in'];
        $state['new_sms_otp_attempts'] = 0;
    } else {
        unset($state['new_sms_otp_token']);
        $state['new_sms_otp_expiry'] = 0;
        $state['new_sms_otp_attempts'] = 0;
    }

    return $state;
}


function auth_check_otp(&$state, $submitted) {
    $submitted = preg_replace('/\D+/', '', (string)$submitted);

    if (strlen($submitted) !== 6) {
        return array(false, 'Please enter a valid 6 digit OTP.');
    }

    if (empty($state['otp_hash']) || empty($state['otp_expiry']) || (int)$state['otp_expiry'] < time()) {
        return array(false, 'OTP expired. Please request a new OTP.');
    }

    if ((int)($state['otp_attempts'] ?? 0) >= 5) {
        return array(false, 'Too many invalid OTP attempts. Please restart.');
    }

    if (!hash_equals((string)$state['otp_hash'], hash('sha256', $submitted))) {
        $state['otp_attempts'] = (int)($state['otp_attempts'] ?? 0) + 1;
        return array(false, 'Invalid OTP. Please try again.');
    }

    $state['verified'] = true;
    unset($state['otp_hash']);
    $state['otp_expiry'] = 0;
    return array(true, 'OTP verified successfully.');
}

function auth_verify_multi_otp($siteData, &$state, $submitted) {
    // First verify the OTP delivered through WhatsApp/email.
    list($valid, $message) = auth_check_otp($state, $submitted);
    if ($valid) {
        $state['verified_channel'] = 'whatsapp_email';
        $state['new_sms_otp_expiry'] = 0;
        unset($state['new_sms_otp_token']);
        return array(true, $message);
    }

    // If that OTP does not match, fall back to the New SMS provider OTP.
    if (!empty($state['new_sms_available']) && !empty($state['new_sms_otp_token'])) {
        list($smsValid, $smsMessage) = auth_external_verify_otp($siteData, $state, $submitted);
        if ($smsValid) {
            $state['verified_channel'] = 'new_sms';
            return array(true, $smsMessage);
        }
        return array(false, $smsMessage);
    }

    return array(false, $message);
}

function auth_activation_link($userId, $siteData) {
    $token = aes_encrypt($userId, $siteData['user_id']);
    return rtrim((string)$siteData['protocol'] . (string)$siteData['baseurl'], '/')
        . '/members/selfactivation?token=' . urlencode($token);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['auth_action'])) {
    try {
        $postedCsrf = (string)($_POST['csrf_token'] ?? '');
        if ($postedCsrf === '' || !hash_equals((string)$_SESSION['auth_csrf'], $postedCsrf)) {
            auth_out(false, 'Security token expired. Please refresh the page.');
        }

        $action = trim((string)$_POST['auth_action']);

        if ($action === 'login') {
            $username = trim(strip_tags((string)($_POST['username'] ?? '')));
            $password = (string)($_POST['password'] ?? '');

            if ($username === '' || $password === '') {
                auth_out(false, 'Please enter username and password.');
            }

            // When the new provider is enabled, tell login() not to send its old OTP.
            // Add the small guard from function_php_login_otp_guard.txt inside login().
            if (auth_new_sms_enabled($site_data)) {
                $_SESSION['auth_suppress_old_login_otp_send'] = true;
            }

            try {
                $login = login($username, $password);
            } finally {
                unset($_SESSION['auth_suppress_old_login_otp_send']);
            }

            if (empty($login['status'])) {
                auth_out(false, (string)($login['message'] ?? 'Invalid login details.'));
            }

            $_SESSION['login_token'] = $login['login_token'];

            if (($login['is_otp'] ?? 'NO') === 'YES') {
                if (auth_new_sms_enabled($site_data)) {
                    $member = auth_member_login_identifier($username);
                    $mobile = auth_mobile_value($member['mobile'] ?? '');
                    $email = auth_email_value($member['email'] ?? '');
                    if (!$member || $mobile === '') {
                        auth_out(false, 'A valid mobile number is not available for OTP login.');
                    }

                    list($oldState, $oldOtp) = auth_new_otp_state(array(
                        'login_token' => $login['login_token'],
                        'member_id' => (int)$member['user_id'],
                        'mobile' => $mobile,
                        'email' => $email
                    ));

                    $name = trim((string)($member['name'] ?? 'Member')) ?: 'Member';
                    $message = "Dear {$name}, your login OTP is {$oldOtp}. Valid for 10 minutes. Do not share this OTP.";
                    send_sms_credentials($mobile, $message);
                    if ($email !== '') {
                        send_email($email, 'Login OTP', $message);
                    }

                    $send = auth_external_send_otp($site_data, $mobile);
                    if (empty($send['status'])) {
                        error_log('LOGIN NEW SMS SEND FAILED: ' . (string)($send['message'] ?? 'Unknown error'));
                    }

                    $_SESSION['auth_login'] = auth_multi_state($oldState, $send);

                    $sentMessage = !empty($send['status'])
                        ? 'Login OTP sent to your registered WhatsApp, email and Inbox SMS.'
                        : 'Login OTP sent to your registered WhatsApp and email. New SMS is temporarily unavailable.';

                    auth_out(true, $sentMessage, 'show_login_otp', array(
                        'expires_in' => 600,
                        'resend_in' => 60
                    ));
                }

                $_SESSION['auth_login'] = array(
                    'provider' => 'old',
                    'stage' => 'otp',
                    'login_token' => $login['login_token'],
                    'last_resend_at' => time()
                );
                auth_out(true, 'Login OTP sent successfully.', 'show_login_otp', array('expires_in' => 300, 'resend_in' => 60));
            }

            session_regenerate_id(true);
            $_SESSION['is_login'] = true;
            unset($_SESSION['auth_login']);
            auth_out(true, 'Login successful.', 'redirect', array('redirect_url' => 'dashboard'));
        }

        if ($action === 'verify_login_otp') {
            $state = $_SESSION['auth_login'] ?? array();
            $otp = preg_replace('/\D+/', '', (string)($_POST['otp'] ?? ''));
            $loginToken = $state['login_token'] ?? ($_SESSION['login_token'] ?? '');

            if ($loginToken === '' || strlen($otp) !== 6) {
                auth_out(false, 'Please enter a valid OTP.');
            }

            if (($state['provider'] ?? '') === 'multi') {
                list($valid, $message) = auth_verify_multi_otp($site_data, $state, $otp);
                $_SESSION['auth_login'] = $state;
                if (!$valid) {
                    auth_out(false, $message);
                }

                session_regenerate_id(true);
                $_SESSION['is_login'] = true;
                $_SESSION['is_otp'] = false;
                $_SESSION['login_token'] = $loginToken;
                unset($_SESSION['auth_login']);
                auth_out(true, $message, 'redirect', array('redirect_url' => 'dashboard'));
            }

            if (($state['provider'] ?? '') === 'new_sms') {
                list($valid, $message) = auth_external_verify_otp($site_data, $state, $otp);
                $_SESSION['auth_login'] = $state;
                if (!$valid) {
                    auth_out(false, $message);
                }

                session_regenerate_id(true);
                $_SESSION['is_login'] = true;
                $_SESSION['is_otp'] = false;
                $_SESSION['login_token'] = $loginToken;
                unset($_SESSION['auth_login']);
                auth_out(true, $message, 'redirect', array('redirect_url' => 'dashboard'));
            }

            $verify = login_otp_verify($loginToken, (int)$otp);
            if (empty($verify['status'])) {
                auth_out(false, (string)($verify['message'] ?? 'Invalid OTP. Please try again.'));
            }

            session_regenerate_id(true);
            $_SESSION['is_login'] = true;
            $_SESSION['is_otp'] = false;
            $_SESSION['login_token'] = $verify['login_token'];
            unset($_SESSION['auth_login']);
            auth_out(true, 'OTP verified successfully.', 'redirect', array('redirect_url' => 'dashboard'));
        }

        if ($action === 'resend_login_otp') {
            $state = $_SESSION['auth_login'] ?? array();
            $loginToken = $state['login_token'] ?? ($_SESSION['login_token'] ?? '');

            if ($loginToken === '') {
                auth_out(false, 'Login session expired. Please login again.', 'restart_login');
            }

            $last = (int)($state['last_resend_at'] ?? 0);
            if ($last > 0 && time() - $last < 60) {
                auth_out(false, 'Please wait before requesting another OTP.', 'none', array('resend_in' => 60 - (time() - $last)));
            }

            if (($state['provider'] ?? '') === 'multi') {
                $member = auth_member_id((int)($state['member_id'] ?? 0));
                if (!$member) {
                    unset($_SESSION['auth_login']);
                    auth_out(false, 'Account was not found. Please login again.', 'restart_login');
                }

                $mobile = auth_mobile_value($member['mobile'] ?? '');
                $email = auth_email_value($member['email'] ?? '');
                list($oldState, $oldOtp) = auth_new_otp_state(array(
                    'login_token' => $loginToken,
                    'member_id' => (int)$member['user_id'],
                    'mobile' => $mobile,
                    'email' => $email
                ));

                $name = trim((string)($member['name'] ?? 'Member')) ?: 'Member';
                $message = "Dear {$name}, your new login OTP is {$oldOtp}. Valid for 10 minutes. Do not share this OTP.";
                send_sms_credentials($mobile, $message);
                if ($email !== '') {
                    send_email($email, 'Login OTP', $message);
                }

                $send = auth_external_send_otp($site_data, $mobile);
                if (empty($send['status'])) {
                    error_log('LOGIN NEW SMS RESEND FAILED: ' . (string)($send['message'] ?? 'Unknown error'));
                }

                $_SESSION['auth_login'] = auth_multi_state($oldState, $send);
                $sentMessage = !empty($send['status'])
                    ? 'New OTP sent to your registered WhatsApp, email and Inbox SMS.'
                    : 'New OTP sent to your registered WhatsApp and email. New SMS is temporarily unavailable.';

                auth_out(true, $sentMessage, 'show_login_otp', array(
                    'expires_in' => 600,
                    'resend_in' => 60
                ));
            }

            if (($state['provider'] ?? '') === 'new_sms') {
                $send = auth_external_send_otp($site_data, $state['mobile'] ?? '');
                if (empty($send['status'])) {
                    auth_out(false, (string)($send['message'] ?? 'Unable to resend OTP.'));
                }

                $_SESSION['auth_login'] = auth_external_state(array(
                    'login_token' => $loginToken,
                    'member_id' => (int)($state['member_id'] ?? 0),
                    'mobile' => auth_mobile_value($state['mobile'] ?? '')
                ), $send);

                auth_out(true, (string)$send['message'], 'show_login_otp', array(
                    'expires_in' => (int)$send['expires_in'],
                    'resend_in' => 60
                ));
            }

            $result = resend_login_otp($loginToken);
            if (empty($result['status'])) {
                auth_out(false, (string)($result['message'] ?? 'Unable to resend OTP.'));
            }

            $_SESSION['auth_login']['last_resend_at'] = time();
            auth_out(true, (string)($result['message'] ?? 'OTP resent successfully.'), 'show_login_otp', array('expires_in' => 300, 'resend_in' => 60));
        }

        if ($action === 'reset_login') {
            unset($_SESSION['auth_login']);
            auth_out(true, 'Login form reset.', 'restart_login');
        }

        if ($action === 'forgot_send_otp') {
            $identifier = trim((string)($_POST['identifier'] ?? ''));
            $member = auth_member_identifier($identifier);

            if (!$member) {
                auth_out(false, 'Registered mobile number or email was not found.');
            }

            $mobile = auth_mobile_value($member['mobile'] ?? '');
            $email = auth_email_value($member['email'] ?? '');

            if ($mobile === '' && $email === '') {
                auth_out(false, 'No valid mobile number or email is available for this account.');
            }

            if (auth_new_sms_enabled($site_data)) {
                if ($mobile === '') {
                    auth_out(false, 'A valid registered mobile number is required for password recovery.');
                }

                list($oldState, $oldOtp) = auth_new_otp_state(array(
                    'member_id' => (int)$member['user_id'],
                    'identifier' => $identifier,
                    'mobile' => $mobile,
                    'email' => $email
                ));

                $name = trim((string)($member['name'] ?? 'Member')) ?: 'Member';
                $message = "Dear {$name}, your password recovery OTP is {$oldOtp}. Valid for 10 minutes. Do not share this OTP.";
                send_sms_credentials($mobile, $message);
                if ($email !== '') {
                    send_email($email, 'Password Recovery OTP', $message);
                }

                $send = auth_external_send_otp($site_data, $mobile);
                if (empty($send['status'])) {
                    error_log('FORGOT NEW SMS SEND FAILED: ' . (string)($send['message'] ?? 'Unknown error'));
                }

                $_SESSION['auth_forgot'] = auth_multi_state($oldState, $send);
                $sentMessage = !empty($send['status'])
                    ? 'Recovery OTP sent to your registered WhatsApp, email and Inbox SMS.'
                    : 'Recovery OTP sent to your registered WhatsApp and email. New SMS is temporarily unavailable.';

                auth_out(true, $sentMessage, 'show_forgot_otp', array(
                    'expires_in' => 600,
                    'resend_in' => 60
                ));
            }

            list($state, $otp) = auth_new_otp_state(array(
                'provider' => 'old',
                'stage' => 'otp',
                'member_id' => (int)$member['user_id'],
                'identifier' => $identifier,
                'mobile' => $mobile,
                'email' => $email
            ));
            $_SESSION['auth_forgot'] = $state;

            $name = trim((string)($member['name'] ?? 'Member'));
            if ($name === '') {
                $name = 'Member';
            }
            $message = "Dear {$name}, your password recovery OTP is {$otp}. Valid for 10 minutes. Do not share this OTP.";

            if ($mobile !== '') {
                send_sms_credentials($mobile, $message);
            }
            if ($email !== '') {
                send_email($email, 'Password Recovery OTP', $message);
            }

            auth_out(true, 'OTP sent to your registered mobile number and email.', 'show_forgot_otp', array('expires_in' => 600, 'resend_in' => 60));
        }

        if ($action === 'forgot_resend_otp') {
            $old = $_SESSION['auth_forgot'] ?? array();
            if (empty($old['member_id'])) {
                auth_out(false, 'Recovery session expired. Please start again.', 'restart_forgot');
            }

            $last = (int)($old['last_resend_at'] ?? 0);
            if ($last > 0 && time() - $last < 60) {
                auth_out(false, 'Please wait before requesting another OTP.', 'none', array('resend_in' => 60 - (time() - $last)));
            }

            $member = auth_member_id($old['member_id']);
            if (!$member) {
                unset($_SESSION['auth_forgot']);
                auth_out(false, 'Account was not found. Please start again.', 'restart_forgot');
            }

            $mobile = auth_mobile_value($member['mobile'] ?? '');
            $email = auth_email_value($member['email'] ?? '');

            if (($old['provider'] ?? '') === 'multi') {
                if ($mobile === '') {
                    auth_out(false, 'A valid registered mobile number is required for password recovery.');
                }

                list($oldState, $oldOtp) = auth_new_otp_state(array(
                    'member_id' => (int)$member['user_id'],
                    'identifier' => $old['identifier'] ?? '',
                    'mobile' => $mobile,
                    'email' => $email
                ));

                $name = trim((string)($member['name'] ?? 'Member')) ?: 'Member';
                $message = "Dear {$name}, your new password recovery OTP is {$oldOtp}. Valid for 10 minutes. Do not share this OTP.";
                send_sms_credentials($mobile, $message);
                if ($email !== '') {
                    send_email($email, 'Password Recovery OTP', $message);
                }

                $send = auth_external_send_otp($site_data, $mobile);
                if (empty($send['status'])) {
                    error_log('FORGOT NEW SMS RESEND FAILED: ' . (string)($send['message'] ?? 'Unknown error'));
                }

                $_SESSION['auth_forgot'] = auth_multi_state($oldState, $send);
                $sentMessage = !empty($send['status'])
                    ? 'New OTP sent to your registered WhatsApp, email and Inbox SMS.'
                    : 'New OTP sent to your registered WhatsApp and email. New SMS is temporarily unavailable.';

                auth_out(true, $sentMessage, 'show_forgot_otp', array(
                    'expires_in' => 600,
                    'resend_in' => 60
                ));
            }

            if (($old['provider'] ?? '') === 'new_sms') {
                $send = auth_external_send_otp($site_data, $mobile);
                if (empty($send['status'])) {
                    auth_out(false, (string)($send['message'] ?? 'Unable to resend recovery OTP.'));
                }

                $_SESSION['auth_forgot'] = auth_external_state(array(
                    'member_id' => (int)$member['user_id'],
                    'identifier' => $old['identifier'] ?? '',
                    'mobile' => $mobile,
                    'email' => $email
                ), $send);

                auth_out(true, (string)$send['message'], 'show_forgot_otp', array(
                    'expires_in' => (int)$send['expires_in'],
                    'resend_in' => 60
                ));
            }

            list($state, $otp) = auth_new_otp_state(array(
                'provider' => 'old',
                'stage' => 'otp',
                'member_id' => (int)$member['user_id'],
                'identifier' => $old['identifier'] ?? '',
                'mobile' => $mobile,
                'email' => $email
            ));
            $_SESSION['auth_forgot'] = $state;

            $name = trim((string)($member['name'] ?? 'Member')) ?: 'Member';
            $message = "Dear {$name}, your new password recovery OTP is {$otp}. Valid for 10 minutes. Do not share this OTP.";
            if ($mobile !== '') {
                send_sms_credentials($mobile, $message);
            }
            if ($email !== '') {
                send_email($email, 'Password Recovery OTP', $message);
            }

            auth_out(true, 'New OTP sent successfully.', 'show_forgot_otp', array('expires_in' => 600, 'resend_in' => 60));
        }

        if ($action === 'forgot_verify_otp') {
            $state = $_SESSION['auth_forgot'] ?? array();
            if (empty($state['member_id'])) {
                auth_out(false, 'Recovery session expired. Please start again.', 'restart_forgot');
            }

            if (($state['provider'] ?? '') === 'multi') {
                list($valid, $otpMessage) = auth_verify_multi_otp($site_data, $state, $_POST['otp'] ?? '');
                if (!$valid) {
                    $_SESSION['auth_forgot'] = $state;
                    auth_out(false, $otpMessage);
                }

                $state['stage'] = 'password';
                $_SESSION['auth_forgot'] = $state;
                auth_out(true, $otpMessage, 'show_forgot_password');
            }

            if (($state['provider'] ?? '') === 'new_sms') {
                list($valid, $otpMessage) = auth_external_verify_otp($site_data, $state, $_POST['otp'] ?? '');
                if (!$valid) {
                    $_SESSION['auth_forgot'] = $state;
                    auth_out(false, $otpMessage);
                }

                $state['stage'] = 'password';
                $_SESSION['auth_forgot'] = $state;
                auth_out(true, $otpMessage, 'show_forgot_password');
            }

            list($valid, $otpMessage) = auth_check_otp($state, $_POST['otp'] ?? '');
            $_SESSION['auth_forgot'] = $state;
            if (!$valid) {
                auth_out(false, $otpMessage);
            }

            $member = auth_member_id($state['member_id']);
            if (!$member) {
                unset($_SESSION['auth_forgot']);
                auth_out(false, 'Account was not found. Please start again.', 'restart_forgot');
            }

            $recoveryIdentifier = !empty($member['username']) ? $member['username'] : $member['mobile'];
            $forgotResult = forgotpassword($recoveryIdentifier);

            if (empty($forgotResult['status'])) {
                auth_out(false, (string)($forgotResult['message'] ?? 'Unable to send password. Please contact support.'));
            }

            unset($_SESSION['auth_forgot']);
            auth_out(true, 'Password has been sent successfully to your registered contact.', 'forgot_completed', array('mode' => 'old'));
        }

        if ($action === 'forgot_update_password') {
            $state = $_SESSION['auth_forgot'] ?? array();
            if (
                !in_array(($state['provider'] ?? ''), array('new_sms', 'multi'), true)
                || ($state['stage'] ?? '') !== 'password'
                || empty($state['verified'])
                || empty($state['member_id'])
            ) {
                auth_out(false, 'Please verify the recovery OTP first.', 'restart_forgot');
            }

            $newPassword = (string)($_POST['new_password'] ?? '');
            $confirmPassword = (string)($_POST['confirm_password'] ?? '');

            if (strlen($newPassword) < 8 || !preg_match('/[A-Za-z]/', $newPassword) || !preg_match('/\d/', $newPassword)) {
                auth_out(false, 'Password must contain at least 8 characters, one letter and one number.');
            }
            if (!hash_equals($newPassword, $confirmPassword)) {
                auth_out(false, 'Password and confirmation do not match.');
            }

            $member = auth_member_id((int)$state['member_id']);
            if (!$member) {
                unset($_SESSION['auth_forgot']);
                auth_out(false, 'Account was not found. Please start again.', 'restart_forgot');
            }

            $updated = db_update(
                'members',
                array('password' => real_escape_string($newPassword)),
                "user_id='" . (int)$state['member_id'] . "'"
            );

            if ($updated === false) {
                auth_out(false, 'Unable to update password. Please try again.');
            }

            unset($_SESSION['auth_forgot']);
            auth_out(true, 'Your password has been updated successfully.', 'forgot_completed', array('mode' => 'new'));
        }

        if ($action === 'reset_forgot') {
            unset($_SESSION['auth_forgot']);
            auth_out(true, 'Recovery form reset.', 'restart_forgot');
        }

        if ($action === 'create_send_otp') {
            if (($site_data['self_registertion'] ?? '') !== 'active') {
                auth_out(false, 'Self registration is currently unavailable.');
            }

            $mobile = auth_mobile_value($_POST['mobile'] ?? '');
            $email = auth_email_value($_POST['email'] ?? '');

            if ($mobile === '' || $email === '') {
                auth_out(false, 'Please enter a valid mobile number and email address.');
            }

            $member = auth_member_contact($mobile, $email);
            $mode = 'new';
            $memberId = 0;

            if ($member) {
                $status = strtolower(trim((string)($member['status'] ?? '')));
                if ($status !== 'unpaid') {
                    auth_out(false, 'Mobile number or email is already registered.');
                }
                $mode = 'reactivation';
                $memberId = (int)$member['user_id'];
                $mobile = auth_mobile_value($member['mobile'] ?? $mobile);
                $email = auth_email_value($member['email'] ?? $email);
            }

            if (auth_new_sms_enabled($site_data)) {
                list($oldState, $oldOtp) = auth_new_otp_state(array(
                    'mobile' => $mobile,
                    'email' => $email,
                    'mode' => $mode,
                    'existing_member_id' => $memberId
                ));

                $message = "Dear Member, your registration OTP is {$oldOtp}. Valid for 10 minutes. Do not share this OTP.";
                send_sms_credentials($mobile, $message);
                send_email($email, 'Registration OTP', $message);

                $send = auth_external_send_otp($site_data, $mobile);
                if (empty($send['status'])) {
                    error_log('REGISTRATION NEW SMS SEND FAILED: ' . (string)($send['message'] ?? 'Unknown error'));
                }

                $_SESSION['auth_register'] = auth_multi_state($oldState, $send);
                $sentMessage = !empty($send['status'])
                    ? 'Registration OTP sent to WhatsApp, email and Inbox SMS.'
                    : 'Registration OTP sent to WhatsApp and email. New SMS is temporarily unavailable.';

                auth_out(true, $sentMessage, 'show_registration_otp', array(
                    'expires_in' => 600,
                    'resend_in' => 60
                ));
            }

            list($state, $otp) = auth_new_otp_state(array(
                'provider' => 'old',
                'stage' => 'otp',
                'mobile' => $mobile,
                'email' => $email,
                'mode' => $mode,
                'existing_member_id' => $memberId
            ));
            $_SESSION['auth_register'] = $state;

            $message = "Dear Member, your registration OTP is {$otp}. Valid for 10 minutes. Do not share this OTP.";
            send_sms_credentials($mobile, $message);
            send_email($email, 'Registration OTP', $message);

            auth_out(true, 'OTP sent to mobile and email.', 'show_registration_otp', array('expires_in' => 600, 'resend_in' => 60));
        }

        if ($action === 'create_resend_otp') {
            $old = $_SESSION['auth_register'] ?? array();
            if (empty($old['mobile']) || empty($old['email'])) {
                auth_out(false, 'Registration session expired. Please start again.', 'restart_registration');
            }

            $last = (int)($old['last_resend_at'] ?? 0);
            if ($last > 0 && time() - $last < 60) {
                auth_out(false, 'Please wait before requesting another OTP.', 'none', array('resend_in' => 60 - (time() - $last)));
            }

            if (($old['provider'] ?? '') === 'multi') {
                list($oldState, $oldOtp) = auth_new_otp_state(array(
                    'mobile' => $old['mobile'],
                    'email' => $old['email'],
                    'mode' => $old['mode'] ?? 'new',
                    'existing_member_id' => (int)($old['existing_member_id'] ?? 0)
                ));

                $message = "Dear Member, your new registration OTP is {$oldOtp}. Valid for 10 minutes. Do not share this OTP.";
                send_sms_credentials($old['mobile'], $message);
                send_email($old['email'], 'Registration OTP', $message);

                $send = auth_external_send_otp($site_data, $old['mobile']);
                if (empty($send['status'])) {
                    error_log('REGISTRATION NEW SMS RESEND FAILED: ' . (string)($send['message'] ?? 'Unknown error'));
                }

                $_SESSION['auth_register'] = auth_multi_state($oldState, $send);
                $sentMessage = !empty($send['status'])
                    ? 'New OTP sent to WhatsApp, email and Inbox SMS.'
                    : 'New OTP sent to WhatsApp and email. New SMS is temporarily unavailable.';

                auth_out(true, $sentMessage, 'show_registration_otp', array(
                    'expires_in' => 600,
                    'resend_in' => 60
                ));
            }

            if (($old['provider'] ?? '') === 'new_sms') {
                $send = auth_external_send_otp($site_data, $old['mobile']);
                if (empty($send['status'])) {
                    auth_out(false, (string)($send['message'] ?? 'Unable to resend registration OTP.'));
                }

                $_SESSION['auth_register'] = auth_external_state(array(
                    'mobile' => $old['mobile'],
                    'email' => $old['email'],
                    'mode' => $old['mode'] ?? 'new',
                    'existing_member_id' => (int)($old['existing_member_id'] ?? 0)
                ), $send);

                auth_out(true, (string)$send['message'], 'show_registration_otp', array(
                    'expires_in' => (int)$send['expires_in'],
                    'resend_in' => 60
                ));
            }

            list($state, $otp) = auth_new_otp_state(array(
                'provider' => 'old',
                'stage' => 'otp',
                'mobile' => $old['mobile'],
                'email' => $old['email'],
                'mode' => $old['mode'] ?? 'new',
                'existing_member_id' => (int)($old['existing_member_id'] ?? 0)
            ));
            $_SESSION['auth_register'] = $state;

            $message = "Dear Member, your new registration OTP is {$otp}. Valid for 10 minutes. Do not share this OTP.";
            send_sms_credentials($state['mobile'], $message);
            send_email($state['email'], 'Registration OTP', $message);

            auth_out(true, 'New OTP sent to mobile and email.', 'show_registration_otp', array('expires_in' => 600, 'resend_in' => 60));
        }

        if ($action === 'create_verify_otp') {
            $state = $_SESSION['auth_register'] ?? array();
            if (empty($state['mobile']) || empty($state['email'])) {
                auth_out(false, 'Registration session expired. Please start again.', 'restart_registration');
            }

            if (($state['provider'] ?? '') === 'multi') {
                list($valid, $otpMessage) = auth_verify_multi_otp($site_data, $state, $_POST['otp'] ?? '');
            } elseif (($state['provider'] ?? '') === 'new_sms') {
                list($valid, $otpMessage) = auth_external_verify_otp($site_data, $state, $_POST['otp'] ?? '');
            } else {
                list($valid, $otpMessage) = auth_check_otp($state, $_POST['otp'] ?? '');
            }

            $_SESSION['auth_register'] = $state;
            if (!$valid) {
                auth_out(false, $otpMessage);
            }

            if (($state['mode'] ?? '') === 'reactivation') {
                $member = auth_member_id($state['existing_member_id'] ?? 0);
                if (!$member || strtolower(trim((string)$member['status'])) !== 'unpaid') {
                    unset($_SESSION['auth_register']);
                    auth_out(false, 'Unable to continue account activation.', 'restart_registration');
                }

                $activationLink = auth_activation_link($member['user_id'], $site_data);
                $name = trim((string)($member['name'] ?? 'Member')) ?: 'Member';
                $message = "Dear {$name}, Please complete your account activation payment by clicking the link below {$activationLink}";

                if (!empty($member['mobile'])) {
                    send_sms_credentials($member['mobile'], $message);
                }
                if (!empty($member['email'])) {
                    send_email($member['email'], 'Account Activation', $message);
                }

                unset($_SESSION['auth_register']);
                auth_out(true, 'Verification successful. Redirecting to activation.', 'activation_redirect', array('redirect_url' => $activationLink));
            }

            $_SESSION['auth_register']['stage'] = 'details';
            auth_out(true, 'OTP verified successfully.', 'show_registration_form');
        }

        if ($action === 'create_account') {
            $state = $_SESSION['auth_register'] ?? array();
            if (empty($state['verified']) || ($state['stage'] ?? '') !== 'details' || ($state['mode'] ?? '') !== 'new') {
                auth_out(false, 'Please verify registration OTP first.', 'restart_registration');
            }

            $name = trim((string)($_POST['name'] ?? ''));
            $userType = trim((string)($_POST['usertype'] ?? ''));
            $password = (string)($_POST['password'] ?? '');
            $confirmPassword = (string)($_POST['confirm_password'] ?? '');
            $terms = (string)($_POST['terms'] ?? '0');

            if (mb_strlen($name) < 2 || mb_strlen($name) > 100) {
                auth_out(false, 'Please enter a valid full name.');
            }
            if (!in_array($userType, array('Retailer', 'Distributor', 'Master Distributor'), true)) {
                auth_out(false, 'Please select a valid user type.');
            }
            if (strlen($password) < 8 || !preg_match('/[A-Za-z]/', $password) || !preg_match('/\d/', $password)) {
                auth_out(false, 'Password must contain at least 8 characters, one letter and one number.');
            }
            if (!hash_equals($password, $confirmPassword)) {
                auth_out(false, 'Password and confirmation do not match.');
            }
            if ($terms !== '1') {
                auth_out(false, 'Please accept the Terms and Conditions.');
            }

            $mobile = auth_mobile_value($state['mobile']);
            $email = auth_email_value($state['email']);
            if ($mobile === '' || $email === '') {
                auth_out(false, 'Verified contact details are invalid.', 'restart_registration');
            }

            if (auth_member_contact($mobile, $email)) {
                auth_out(false, 'Mobile number or email is already registered.', 'restart_registration');
            }

            $insData = array(
                'site_id' => $site_data['site_id'],
                'username' => get_username($mobile),
                'password' => real_escape_string($password),
                'is_otp' => 'NO',
                'usertype' => $userType,
                'parent_id' => $site_data['user_id'],
                'parent_uid' => $site_data['username'],
                'name' => real_escape_string($name),
                'company' => real_escape_string($name),
                'mobile' => $mobile,
                'email' => $email,
                'aadhaar' => '000000000000',
                'pan' => '0000000000',
                'balance' => 0,
                'plan_id' => '1',
                'created_at' => current_timestamp(),
                'remark' => '',
                'status' => 'Unpaid'
            );

            $insertId = db_insert('members', $insData);
            if (!$insertId) {
                auth_out(false, 'Unable to create account. Please try again.');
            }

            $activationLink = auth_activation_link($insertId, $site_data);
            $message = "Dear {$name}, Please activate your account by clicking the link below {$activationLink}";
            send_sms_credentials($mobile, $message);
            send_email($email, 'Account Activation', $message);

            unset($_SESSION['auth_register']);
            auth_out(true, 'Account created successfully.', 'activation_redirect', array('redirect_url' => $activationLink));
        }

        if ($action === 'reset_registration') {
            unset($_SESSION['auth_register']);
            auth_out(true, 'Registration form reset.', 'restart_registration');
        }

        auth_out(false, 'Unknown request. Please refresh the page.');
    } catch (Throwable $e) {
        error_log('AUTH ERROR: ' . $e->getMessage());
        auth_out(false, 'Server error occurred. Please check the PHP error log.');
    }
}

$newSmsServiceEnabled = auth_new_sms_enabled($site_data);
$brandName = !empty($site_data['brand']) ? $site_data['brand'] : 'Secure Portal';
$brandLogo = !empty($site_data['logo']) ? $site_data['logo'] : '';
$escape = function ($value) {
    return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
};

$frontState = array(
    'login_stage' => $_SESSION['auth_login']['stage'] ?? '',
    'forgot_stage' => $_SESSION['auth_forgot']['stage'] ?? '',
    'register_stage' => $_SESSION['auth_register']['stage'] ?? ''
);
$csrfToken = $_SESSION['auth_csrf'];
$currentYear = date('Y');
$e = $escape;
$state = $frontState;

$registrationEnabled = (
    isset($site_data['self_registertion'])
    && strtolower(trim((string)$site_data['self_registertion'])) === 'active'
);
?>
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="color-scheme" content="light">
    <title>Secure Access | <?=$e($brandName)?></title>
    <?php if ($brandLogo !== ''): ?>
        <link rel="icon" href="<?=$e($brandLogo)?>">
    <?php endif; ?>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" rel="stylesheet">
    <link href="/layouts/members/assets/stylesheets/front.css" rel="stylesheet">
</head>
<body class="loading">
    <div id="pageLoader" class="page-loader" role="status" aria-live="polite" aria-label="Loading">
        <div class="loader-content">
            <div class="loader-mark">
                <span class="loader-ring" aria-hidden="true"></span>
                <div class="loader-logo-shell">
                    <?php if ($brandLogo !== ''): ?>
                        <img id="loaderLogo" class="loader-logo brand-img" src="<?=$e($brandLogo)?>" alt="<?=$e($brandName)?>">
                    <?php endif; ?>
                    <span id="loaderLogoFallback" class="logo-fallback" <?= $brandLogo !== '' ? 'hidden' : '' ?>><?=$e(mb_substr($brandName, 0, 1))?></span>
                </div>
            </div>
            <div class="loader-name"><?=$e($brandName)?></div>
        </div>
    </div>

    <div class="toast-stack" id="toastStack" aria-live="polite" aria-atomic="true"></div>

    <main class="auth-shell">
        <section class="auth-card" aria-label="Account access">
            <aside class="visual" aria-label="<?=$e($brandName)?>">
                <div class="brand-block">
                    <div class="brand-mark">
                        <?php if ($brandLogo !== ''): ?>
                            <img class="brand-img" src="<?=$e($brandLogo)?>" alt="<?=$e($brandName)?>">
                        <?php endif; ?>
                        <span class="logo-fallback" <?= $brandLogo !== '' ? 'hidden' : '' ?>><?=$e(mb_substr($brandName, 0, 1))?></span>
                    </div>
                    <div class="brand-name"><?=$e($brandName)?></div>
                    <h1 class="brand-title">Secure access, Manage.</h1>
                    <p class="brand-copy">Sign in or create an account to continue.</p>
                </div>
            </aside>

            <section class="form-side">
                <nav class="tabs" role="tablist" aria-label="Authentication options">
                    <button type="button" class="tab-btn active" id="tab-login" data-tab="login" role="tab" aria-selected="true" aria-controls="panel-login">Login</button>
                    <button type="button" class="tab-btn" id="tab-forgot" data-tab="forgot" role="tab" aria-selected="false" aria-controls="panel-forgot" tabindex="-1">Forgot Password</button>
                    <?php if ($registrationEnabled): ?>
                    <button type="button" class="tab-btn" id="tab-register" data-tab="register" role="tab" aria-selected="false" aria-controls="panel-register" tabindex="-1">Create Account</button>
                    <?php endif; ?>
                </nav>

                <div class="panels">
                    <div id="panel-login" class="auth-panel active" role="tabpanel" aria-labelledby="tab-login">
                        <div id="loginMain">
                            <div class="heading">
                                <h2>Welcome Back</h2>
                                <p>Enter your details to access your account.</p>
                            </div>

                            <form id="loginForm" novalidate>
                                <div class="field">
                                    <label for="loginUsername">Mobile Number or Username</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-user" aria-hidden="true"></i>
                                        <input id="loginUsername" name="username" type="text" placeholder="Enter mobile number or username" maxlength="100" autocomplete="username" required>
                                    </div>
                                </div>

                                <div class="field">
                                    <label for="loginPassword">Password</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-lock" aria-hidden="true"></i>
                                        <input id="loginPassword" name="password" type="password" placeholder="Enter your password" maxlength="128" autocomplete="current-password" required>
                                        <button type="button" class="password-toggle" data-target="loginPassword" aria-label="Show password">
                                            <i class="fa-solid fa-eye" aria-hidden="true"></i>
                                        </button>
                                    </div>
                                </div>

                                <div class="form-meta">
                                    <label class="check-label" for="remember">
                                        <input type="checkbox" id="remember">
                                        <span>Remember me</span>
                                    </label>
                                    <button type="button" class="secondary-link" data-open="forgot">Forgot password?</button>
                                </div>

                                <button class="btn btn-gradient w-100" type="submit">
                                    <span>Sign In</span>
                                    <i class="fa-solid fa-arrow-right" aria-hidden="true"></i>
                                </button>
                            </form>
                        </div>

                        <div id="loginOtp" hidden>
                            <div class="heading">
                                <h2>Verify OTP</h2>
                                <p>Enter the six-digit code sent to your registered contact.</p>
                            </div>
                            <div class="otp-group" data-otp="login"></div>
                            <p class="otp-expiry" id="loginExpiry"></p>
                            <div class="d-grid gap-2">
                                <button type="button" class="btn btn-gradient" id="verifyLoginOtp">Verify OTP</button>
                                <button type="button" class="btn btn-outline-primary" id="resendLoginOtp">Resend OTP</button>
                                <button type="button" class="secondary-link" id="resetLogin">Change login details</button>
                            </div>
                        </div>
                    </div>

                    <div id="panel-forgot" class="auth-panel" role="tabpanel" aria-labelledby="tab-forgot" hidden>
                        <div class="stepper" id="forgotStepper" aria-label="Password recovery progress">
                            <div class="step active" data-step="1">Account</div>
                            <div class="step" data-step="2">OTP</div>
                            <div class="step" data-step="3">New Password</div>
                        </div>

                        <div id="forgotMobile">
                            <div class="heading">
                                <h2>Recover Password</h2>
                                <p>Use your registered mobile number or email address.</p>
                            </div>
                            <form id="forgotForm">
                                <div class="field">
                                    <label for="forgotIdentifier">Mobile Number or Email</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-user-check" aria-hidden="true"></i>
                                        <input id="forgotIdentifier" type="text" placeholder="Enter registered mobile or email" maxlength="150" autocomplete="username" required>
                                    </div>
                                </div>
                                <button class="btn btn-gradient w-100" type="submit">Send Recovery OTP</button>
                            </form>
                        </div>

                        <div id="forgotOtp" hidden>
                            <div class="heading">
                                <h2>Verify OTP</h2>
                                <p>Enter the six-digit recovery code we sent you.</p>
                            </div>
                            <div class="otp-group" data-otp="forgot"></div>
                            <p class="otp-expiry" id="forgotExpiry"></p>
                            <div class="d-grid gap-2">
                                <button type="button" class="btn btn-gradient" id="verifyForgotOtp">Verify OTP</button>
                                <button type="button" class="btn btn-outline-primary" id="resendForgotOtp">Resend OTP</button>
                                <button type="button" class="secondary-link" id="resetForgot">Change mobile or email</button>
                            </div>
                        </div>

                        <div id="forgotPassword" hidden>
                            <div class="heading">
                                <h2>Create New Password</h2>
                                <p>OTP verified. Enter and save your new account password.</p>
                            </div>
                            <form id="forgotPasswordForm">
                                <div class="field">
                                    <label for="forgotNewPassword">New Password</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-lock" aria-hidden="true"></i>
                                        <input id="forgotNewPassword" type="password" placeholder="Enter new password" maxlength="128" autocomplete="new-password" required>
                                        <button type="button" class="password-toggle" data-target="forgotNewPassword" aria-label="Show password">
                                            <i class="fa-solid fa-eye" aria-hidden="true"></i>
                                        </button>
                                    </div>
                                    <p class="field-hint">Use 8+ characters with at least one letter and one number.</p>
                                </div>

                                <div class="field">
                                    <label for="forgotConfirmPassword">Confirm New Password</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-lock" aria-hidden="true"></i>
                                        <input id="forgotConfirmPassword" type="password" placeholder="Re-enter new password" maxlength="128" autocomplete="new-password" required>
                                        <button type="button" class="password-toggle" data-target="forgotConfirmPassword" aria-label="Show password">
                                            <i class="fa-solid fa-eye" aria-hidden="true"></i>
                                        </button>
                                    </div>
                                </div>

                                <button class="btn btn-gradient w-100" type="submit">Save New Password</button>
                                <button type="button" class="secondary-link w-100 mt-2" id="resetForgotPassword">Start Again</button>
                            </form>
                        </div>

                        <div id="forgotDone" hidden></div>
                    </div>

                    <div id="panel-register" class="auth-panel" role="tabpanel" aria-labelledby="tab-register" hidden>
                        <div class="stepper" id="registerStepper" aria-label="Registration progress">
                            <div class="step active" data-step="1">Contact</div>
                            <div class="step" data-step="2">OTP</div>
                            <div class="step" data-step="3">Details</div>
                            <div class="step" data-step="4">Activate</div>
                        </div>

                        <div id="registerContact">
                            <div class="heading">
                                <h2>Create Account</h2>
                                <p>Start with your mobile number and email address.</p>
                            </div>
                            <form id="registerContactForm">
                                <div class="field">
                                    <label for="registerMobile">Mobile Number</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-mobile-screen-button" aria-hidden="true"></i>
                                        <input id="registerMobile" type="tel" placeholder="Enter 10-digit mobile number" inputmode="numeric" maxlength="10" autocomplete="tel" required>
                                    </div>
                                </div>

                                <div class="field">
                                    <label for="registerEmail">Email Address</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-envelope" aria-hidden="true"></i>
                                        <input id="registerEmail" type="email" placeholder="Enter your email address" maxlength="150" autocomplete="email" required>
                                    </div>
                                </div>

                                <button class="btn btn-gradient w-100" type="submit">Send Registration OTP</button>
                            </form>
                        </div>

                        <div id="registerOtp" hidden>
                            <div class="heading">
                                <h2>Verify OTP</h2>
                                <p>Enter the code sent to your mobile and email.</p>
                            </div>
                            <div class="otp-group" data-otp="register"></div>
                            <p class="otp-expiry" id="registerExpiry"></p>
                            <div class="d-grid gap-2">
                                <button type="button" class="btn btn-gradient" id="verifyRegisterOtp">Verify OTP</button>
                                <button type="button" class="btn btn-outline-primary" id="resendRegisterOtp">Resend OTP</button>
                                <button type="button" class="secondary-link" id="resetRegisterOtp">Restart registration</button>
                            </div>
                        </div>

                        <div id="registerDetails" hidden>
                            <div class="heading">
                                <h2>Account Details</h2>
                                <p>Complete your profile to create the account.</p>
                            </div>
                            <form id="registerDetailsForm">
                                <div class="field">
                                    <label for="fullName">Full Name</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-id-card" aria-hidden="true"></i>
                                        <input id="fullName" type="text" placeholder="Enter your full name" maxlength="100" autocomplete="name" required>
                                    </div>
                                </div>

                                <div class="field">
                                    <label for="userType">User Type</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-briefcase" aria-hidden="true"></i>
                                        <select id="userType" required>
                                            <option value="" selected disabled>Choose account type</option>
                                            <option>Retailer</option>
                                            <option>Distributor</option>
                                            <option>Master Distributor</option>
                                        </select>
                                    </div>
                                </div>

                                <div class="field">
                                    <label for="registerPassword">Password</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-lock" aria-hidden="true"></i>
                                        <input id="registerPassword" type="password" placeholder="Create a password" maxlength="128" autocomplete="new-password" required>
                                        <button type="button" class="password-toggle" data-target="registerPassword" aria-label="Show password">
                                            <i class="fa-solid fa-eye" aria-hidden="true"></i>
                                        </button>
                                    </div>
                                    <p class="field-hint">Use 8+ characters with at least one letter and one number.</p>
                                </div>

                                <div class="password-meter" aria-hidden="true">
                                    <span id="passwordMeter"></span>
                                </div>

                                <div class="field">
                                    <label for="confirmPassword">Confirm Password</label>
                                    <div class="input-wrap">
                                        <i class="field-icon fa-solid fa-lock" aria-hidden="true"></i>
                                        <input id="confirmPassword" type="password" placeholder="Re-enter your password" maxlength="128" autocomplete="new-password" required>
                                        <button type="button" class="password-toggle" data-target="confirmPassword" aria-label="Show password">
                                            <i class="fa-solid fa-eye" aria-hidden="true"></i>
                                        </button>
                                    </div>
                                </div>

                                <label class="check-label mb-3" for="terms">
                                    <input type="checkbox" id="terms">
                                    <span>I accept the Terms and Conditions.</span>
                                </label>

                                <button class="btn btn-gradient w-100" type="submit">Create Account</button>
                                <button class="secondary-link w-100 mt-2" type="button" id="resetRegister">Restart registration</button>
                            </form>
                        </div>
                    </div>
                </div>

                <footer class="footer">Copyright <?=$e($currentYear)?> <?=$e($brandName)?>. All Rights Reserved.</footer>

                <div id="processingOverlay" class="processing-overlay" role="status" aria-live="polite" hidden>
                    <div class="processing-box">
                        <div class="processing-spinner" aria-hidden="true"></div>
                        <h3 id="processingTitle">Processing</h3>
                        <p id="processingMessage">Please keep this page open.</p>
                    </div>
                </div>
            </section>
        </section>
    </main>

    <script>
        const AUTH_CSRF_TOKEN = <?=json_encode($csrfToken, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT)?>;
        const FRONTEND_STATE = <?=json_encode($state, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT)?>;
        let redirecting = false;
        let activeTab = 'login';

        function byId(id) {
            return document.getElementById(id);
        }

        function showPageLoader() {
            const loader = byId('pageLoader');
            if (!loader) {
                return;
            }
            loader.classList.remove('fade');
            document.body.classList.add('loading');
        }

        function hidePageLoader() {
            const loader = byId('pageLoader');
            document.body.classList.remove('loading');

            if (!loader || loader.dataset.closed === 'true') {
                return;
            }

            loader.dataset.closed = 'true';
            loader.classList.add('fade');
            window.setTimeout(function () {
                loader.remove();
            }, 350);
        }

        (function initializePageLoader() {
            showPageLoader();

            const closeLoader = function () {
                window.setTimeout(hidePageLoader, 180);
            };

            if (document.readyState === 'complete') {
                closeLoader();
            } else {
                window.addEventListener('load', closeLoader, { once: true });
            }

            window.setTimeout(hidePageLoader, 6000);
        })();

        function showProcessingOverlay(title, message) {
            byId('processingTitle').textContent = title || 'Processing';
            byId('processingMessage').textContent = message || 'Please keep this page open.';
            byId('processingOverlay').hidden = false;
        }

        function hideProcessingOverlay() {
            byId('processingOverlay').hidden = true;
        }

        function setButtonLoading(button, loading, text) {
            if (!button) {
                return;
            }

            if (loading) {
                if (!button.dataset.originalHtml) {
                    button.dataset.originalHtml = button.innerHTML;
                }

                button.disabled = true;
                button.classList.add('is-loading');
                button.replaceChildren();

                const spinner = document.createElement('span');
                spinner.className = 'button-spinner';
                spinner.setAttribute('aria-hidden', 'true');

                const label = document.createElement('span');
                label.textContent = text || 'Processing...';
                button.append(spinner, label);
                return;
            }

            button.disabled = false;
            button.classList.remove('is-loading');
            if (button.dataset.originalHtml) {
                button.innerHTML = button.dataset.originalHtml;
            }
        }

        function toast(message, type) {
            const toastType = type || 'error';
            const notification = document.createElement('div');
            notification.className = 'app-toast ' + toastType;
            notification.setAttribute('role', toastType === 'success' ? 'status' : 'alert');

            const icon = document.createElement('i');
            icon.className = toastType === 'success'
                ? 'fa-solid fa-circle-check'
                : 'fa-solid fa-circle-exclamation';
            icon.setAttribute('aria-hidden', 'true');

            const text = document.createElement('span');
            text.textContent = message;
            notification.append(icon, text);
            byId('toastStack').appendChild(notification);

            window.setTimeout(function () {
                notification.remove();
            }, 4500);
        }

        function showErrorAnimation(message, target) {
            toast(message, 'error');

            if (!target) {
                return;
            }

            if (target.classList && target.classList.contains('otp-group')) {
                setOtpGroupState(target, 'error');
            } else {
                target.classList.add('invalid-field');
                window.setTimeout(function () {
                    target.classList.remove('invalid-field');
                }, 450);
            }

            if (typeof target.focus === 'function') {
                target.focus();
            }
        }

        function showSuccessAnimation(title, message, options) {
            const settings = options || {};
            const box = document.createElement('div');
            box.className = 'success-view';

            const check = document.createElement('div');
            check.className = 'success-check';
            check.innerHTML = '<i class="fa-solid fa-check" aria-hidden="true"></i>';

            const heading = document.createElement('h3');
            heading.textContent = title;

            const copy = document.createElement('p');
            copy.textContent = message;
            box.append(check, heading, copy);

            if (settings.target) {
                settings.target.replaceChildren(box);
            } else {
                toast(message, 'success');
            }

            return box;
        }

        function safeRedirectUrl(raw) {
            try {
                if (!raw || /^\s*(javascript|data):/i.test(raw)) {
                    return null;
                }

                const url = new URL(raw, window.location.href);
                if (url.origin === window.location.origin) {
                    return url.href;
                }

                return /^https?:$/.test(url.protocol) ? url.href : null;
            } catch (error) {
                return null;
            }
        }

        function secureRedirect(url, message) {
            const safeUrl = safeRedirectUrl(url);
            if (!safeUrl) {
                showErrorAnimation('Unsafe redirect was blocked.');
                return;
            }

            redirecting = true;
            showProcessingOverlay('Success', message || 'Redirecting securely...');
            window.setTimeout(function () {
                window.location.assign(safeUrl);
            }, 1000);
        }

        function setButtonTimer(button, seconds) {
            let remaining = Math.max(0, Number(seconds) || 0);
            window.clearInterval(button._timer);

            const original = button.dataset.cooldownText || button.textContent.trim();
            button.dataset.cooldownText = original;

            function drawTimer() {
                if (remaining > 0) {
                    button.disabled = true;
                    button.textContent = 'Resend available in 00:' + String(remaining).padStart(2, '0');
                    remaining -= 1;
                    return;
                }

                window.clearInterval(button._timer);
                button.disabled = false;
                button.textContent = original;
            }

            drawTimer();
            button._timer = window.setInterval(drawTimer, 1000);
        }

        function startResendCountdown(button, seconds) {
            setButtonTimer(button, seconds);
        }

        function startOtpCountdown(element, seconds) {
            let remaining = Math.max(0, Number(seconds) || 0);
            window.clearInterval(element._timer);

            function drawTimer() {
                const minutes = Math.floor(remaining / 60);
                const secondsPart = remaining % 60;
                element.textContent = 'OTP expires in '
                    + String(minutes).padStart(2, '0')
                    + ':'
                    + String(secondsPart).padStart(2, '0');

                if (remaining <= 0) {
                    window.clearInterval(element._timer);
                    return;
                }

                remaining -= 1;
            }

            drawTimer();
            element._timer = window.setInterval(drawTimer, 1000);
        }

        function setupOtpGroup(container) {
            if (container.children.length) {
                return;
            }

            for (let index = 0; index < 6; index += 1) {
                const input = document.createElement('input');
                input.type = 'text';
                input.inputMode = 'numeric';
                input.maxLength = 1;
                input.autocomplete = index === 0 ? 'one-time-code' : 'off';
                input.setAttribute('aria-label', 'OTP digit ' + (index + 1));

                input.addEventListener('input', function () {
                    input.value = input.value.replace(/\D/g, '').slice(-1);
                    if (input.value && input.nextElementSibling) {
                        input.nextElementSibling.focus();
                    }
                });

                input.addEventListener('keydown', function (event) {
                    if (event.key === 'Backspace' && !input.value && input.previousElementSibling) {
                        input.previousElementSibling.focus();
                    }
                    if (event.key === 'ArrowLeft' && input.previousElementSibling) {
                        input.previousElementSibling.focus();
                    }
                    if (event.key === 'ArrowRight' && input.nextElementSibling) {
                        input.nextElementSibling.focus();
                    }
                });

                input.addEventListener('paste', function (event) {
                    const value = (event.clipboardData.getData('text') || '')
                        .replace(/\D/g, '')
                        .slice(0, 6);

                    if (!value.length) {
                        return;
                    }

                    event.preventDefault();
                    Array.from(container.children).forEach(function (otpInput, otpIndex) {
                        otpInput.value = value[otpIndex] || '';
                    });
                    container.children[Math.min(value.length, 6) - 1].focus();
                });

                container.appendChild(input);
            }
        }

        function getOtpValue(container) {
            return Array.from(container.querySelectorAll('input'))
                .map(function (input) {
                    return input.value;
                })
                .join('');
        }

        function clearOtpGroup(container) {
            container.querySelectorAll('input').forEach(function (input) {
                input.value = '';
            });
        }

        function focusFirstOtp(container) {
            const input = container.querySelector('input');
            if (input) {
                input.focus();
            }
        }

        function setOtpGroupState(container, state) {
            container.classList.remove('error', 'success');
            void container.offsetWidth;
            container.classList.add(state);
        }

        document.querySelectorAll('.otp-group').forEach(setupOtpGroup);

        function switchAuthTab(tabName, focusPanel) {
            if (!['login', 'forgot', 'register'].includes(tabName)) {
                return;
            }

            activeTab = tabName;

            document.querySelectorAll('.tab-btn').forEach(function (button) {
                const selected = button.dataset.tab === tabName;
                button.classList.toggle('active', selected);
                button.setAttribute('aria-selected', selected ? 'true' : 'false');
                button.tabIndex = selected ? 0 : -1;
            });

            document.querySelectorAll('.auth-panel').forEach(function (panel) {
                const selected = panel.id === 'panel-' + tabName;
                panel.classList.toggle('active', selected);
                panel.hidden = !selected;
            });

            if (focusPanel !== false) {
                const firstInput = document.querySelector('#panel-' + tabName + ' input:not([type="hidden"])');
                if (firstInput) {
                    firstInput.focus();
                }
            }
        }

        document.querySelectorAll('[data-tab]').forEach(function (button) {
            button.addEventListener('click', function () {
                switchAuthTab(button.dataset.tab);
            });

            button.addEventListener('keydown', function (event) {
                if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) {
                    return;
                }

                event.preventDefault();
                const tabs = Array.from(document.querySelectorAll('.tab-btn'));
                const currentIndex = tabs.indexOf(button);
                let nextIndex = currentIndex;

                if (event.key === 'ArrowLeft') {
                    nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
                } else if (event.key === 'ArrowRight') {
                    nextIndex = (currentIndex + 1) % tabs.length;
                } else if (event.key === 'Home') {
                    nextIndex = 0;
                } else if (event.key === 'End') {
                    nextIndex = tabs.length - 1;
                }

                switchAuthTab(tabs[nextIndex].dataset.tab, false);
                tabs[nextIndex].focus();
            });
        });

        document.querySelectorAll('[data-open]').forEach(function (button) {
            button.addEventListener('click', function () {
                switchAuthTab(button.dataset.open);
            });
        });

        function showOnly(ids, visibleId) {
            ids.forEach(function (id) {
                byId(id).hidden = id !== visibleId;
            });
        }

        function updateSteps(id, number) {
            document.querySelectorAll('#' + id + ' .step').forEach(function (step, index) {
                const active = index === number - 1;
                step.classList.toggle('done', index < number - 1);
                step.classList.toggle('active', active);
                if (active) {
                    step.setAttribute('aria-current', 'step');
                } else {
                    step.removeAttribute('aria-current');
                }
            });
        }

        function showLoginOtpStep(data) {
            const details = data || {};
            showOnly(['loginMain', 'loginOtp'], 'loginOtp');
            startOtpCountdown(byId('loginExpiry'), details.expires_in || 600);
            startResendCountdown(byId('resendLoginOtp'), details.resend_in || 60);
            focusFirstOtp(document.querySelector('[data-otp="login"]'));
        }

        function showForgotOtpStep(data) {
            const details = data || {};
            switchAuthTab('forgot');
            showOnly(['forgotMobile', 'forgotOtp', 'forgotPassword', 'forgotDone'], 'forgotOtp');
            updateSteps('forgotStepper', 2);
            startOtpCountdown(byId('forgotExpiry'), details.expires_in || 600);
            startResendCountdown(byId('resendForgotOtp'), details.resend_in || 60);
            focusFirstOtp(document.querySelector('[data-otp="forgot"]'));
        }

        function showForgotPasswordStep() {
            switchAuthTab('forgot');
            showOnly(['forgotMobile', 'forgotOtp', 'forgotPassword', 'forgotDone'], 'forgotPassword');
            updateSteps('forgotStepper', 3);
            byId('forgotNewPassword').focus();
        }

        function showForgotCompletedStep(message, data) {
            showOnly(['forgotMobile', 'forgotOtp', 'forgotPassword', 'forgotDone'], 'forgotDone');
            updateSteps('forgotStepper', 3);

            const target = byId('forgotDone');
            const isNewPassword = (data && data.mode === 'new');
            showSuccessAnimation(isNewPassword ? 'Password Updated' : 'Password Sent', message, { target: target });

            const button = document.createElement('button');
            button.type = 'button';
            button.className = 'btn btn-gradient w-100';
            button.textContent = 'Back to Login';
            button.addEventListener('click', function () {
                switchAuthTab('login');
            });
            target.appendChild(button);
        }

        function showRegistrationOtpStep(data) {
            const details = data || {};
            switchAuthTab('register');
            showOnly(['registerContact', 'registerOtp', 'registerDetails'], 'registerOtp');
            updateSteps('registerStepper', 2);
            startOtpCountdown(byId('registerExpiry'), details.expires_in || 600);
            startResendCountdown(byId('resendRegisterOtp'), details.resend_in || 60);
            focusFirstOtp(document.querySelector('[data-otp="register"]'));
        }

        function showRegistrationFormStep() {
            switchAuthTab('register');
            showOnly(['registerContact', 'registerOtp', 'registerDetails'], 'registerDetails');
            updateSteps('registerStepper', 3);
            byId('fullName').focus();
        }

        function processAction(response) {
            const data = response.data || {};

            switch (response.action) {
                case 'none':
                    break;
                case 'redirect':
                    secureRedirect(data.redirect_url);
                    break;
                case 'show_login_otp':
                    showLoginOtpStep(data);
                    break;
                case 'show_forgot_otp':
                    showForgotOtpStep(data);
                    break;
                case 'show_forgot_password':
                    showForgotPasswordStep();
                    break;
                case 'forgot_completed':
                    showForgotCompletedStep(response.message, data);
                    break;
                case 'show_registration_otp':
                    showRegistrationOtpStep(data);
                    break;
                case 'show_registration_form':
                    showRegistrationFormStep(data);
                    break;
                case 'activation_redirect':
                    secureRedirect(data.redirect_url, 'Redirecting to secure account activation...');
                    break;
                case 'restart_login':
                    showOnly(['loginMain', 'loginOtp'], 'loginMain');
                    switchAuthTab('login');
                    break;
                case 'restart_forgot':
                    showOnly(['forgotMobile', 'forgotOtp', 'forgotPassword', 'forgotDone'], 'forgotMobile');
                    updateSteps('forgotStepper', 1);
                    break;
                case 'restart_registration':
                    showOnly(['registerContact', 'registerOtp', 'registerDetails'], 'registerContact');
                    updateSteps('registerStepper', 1);
                    break;
                default:
                    throw new Error('Unsupported API action.');
            }
        }

        async function postApi(endpoint, data, button, options) {
            const requestData = data || {};
            const settings = options || {};
            const actionMap = {
                'api/login.php': 'login',
                'login': 'login',
                'api/verify-login-otp.php': 'verify_login_otp',
                'verify-login-otp': 'verify_login_otp',
                'api/resend-login-otp.php': 'resend_login_otp',
                'resend-login-otp': 'resend_login_otp',
                'api/reset-login.php': 'reset_login',
                'reset-login': 'reset_login',
                'api/forgot-send-otp.php': 'forgot_send_otp',
                'forgot-send-otp': 'forgot_send_otp',
                'api/forgot-verify-otp.php': 'forgot_verify_otp',
                'forgot-verify-otp': 'forgot_verify_otp',
                'api/forgot-resend-otp.php': 'forgot_resend_otp',
                'forgot-resend-otp': 'forgot_resend_otp',
                'api/forgot-update-password.php': 'forgot_update_password',
                'forgot-update-password': 'forgot_update_password',
                'api/reset-forgot.php': 'reset_forgot',
                'reset-forgot': 'reset_forgot',
                'api/create-send-otp.php': 'create_send_otp',
                'create-send-otp': 'create_send_otp',
                'create-resend-otp': 'create_resend_otp',
                'api/create-verify-otp.php': 'create_verify_otp',
                'create-verify-otp': 'create_verify_otp',
                'api/create-account.php': 'create_account',
                'create-account': 'create_account',
                'api/reset-registration.php': 'reset_registration',
                'reset-registration': 'reset_registration'
            };

            const action = actionMap[endpoint] || endpoint;
            const payload = new URLSearchParams();
            payload.append('csrf_token', AUTH_CSRF_TOKEN);
            payload.append('auth_action', action);
            Object.entries(requestData).forEach(function (entry) {
                payload.append(entry[0], String(entry[1] ?? ''));
            });

            const controller = new AbortController();
            const timeout = window.setTimeout(function () {
                controller.abort();
            }, 30000);

            setButtonLoading(button, true, settings.loadingText || 'Processing...');
            if (settings.overlay !== false) {
                showProcessingOverlay(
                    settings.title || 'Processing',
                    settings.message || 'Please keep this page open.'
                );
            }

            try {
                const result = await fetch(window.location.href, {
                    method: 'POST',
                    credentials: 'same-origin',
                    cache: 'no-store',
                    redirect: 'follow',
                    headers: {
                        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                        'Accept': 'application/json',
                        'X-Requested-With': 'XMLHttpRequest'
                    },
                    body: payload.toString(),
                    signal: controller.signal
                });

                const responseText = await result.text();
                let response;

                try {
                    response = JSON.parse(responseText);
                } catch (error) {
                    throw new Error('Invalid server response.');
                }

                if (
                    !response
                    || typeof response.status !== 'boolean'
                    || typeof response.message !== 'string'
                ) {
                    throw new Error('Invalid response structure.');
                }

                if (!response.status) {
                    showErrorAnimation(response.message, settings.target || null);
                    processAction(response);
                    return response;
                }

                toast(response.message, 'success');
                processAction(response);
                return response;
            } catch (error) {
                const message = error.name === 'AbortError'
                    ? 'The request timed out. Please try again.'
                    : 'Unable to process the request. Please try again.';
                showErrorAnimation(message, settings.target || null);
                return null;
            } finally {
                window.clearTimeout(timeout);
                setButtonLoading(button, false);
                if (!redirecting) {
                    hideProcessingOverlay();
                }
            }
        }

        function formButton(event) {
            return event.submitter || event.currentTarget.querySelector('button[type="submit"]');
        }

        byId('loginForm').addEventListener('submit', function (event) {
            event.preventDefault();
            postApi(
                'api/login.php',
                {
                    username: byId('loginUsername').value.trim(),
                    password: byId('loginPassword').value,
                    remember: byId('remember').checked ? 1 : 0
                },
                formButton(event),
                {
                    loadingText: 'Signing in...',
                    target: byId('loginUsername')
                }
            );
        });

        byId('verifyLoginOtp').addEventListener('click', function (event) {
            const group = document.querySelector('[data-otp="login"]');
            postApi(
                'api/verify-login-otp.php',
                { otp: getOtpValue(group) },
                event.currentTarget,
                {
                    loadingText: 'Verifying OTP...',
                    target: group
                }
            ).then(function (response) {
                if (response && !response.status) {
                    setOtpGroupState(group, 'error');
                    clearOtpGroup(group);
                    focusFirstOtp(group);
                }
            });
        });

        byId('resendLoginOtp').addEventListener('click', function (event) {
            postApi(
                'api/resend-login-otp.php',
                {},
                event.currentTarget,
                { loadingText: 'Sending OTP...' }
            );
        });

        byId('resetLogin').addEventListener('click', function (event) {
            postApi(
                'api/reset-login.php',
                {},
                event.currentTarget,
                { overlay: false }
            );
        });

        byId('forgotForm').addEventListener('submit', function (event) {
            event.preventDefault();
            postApi(
                'api/forgot-send-otp.php',
                { identifier: byId('forgotIdentifier').value.trim() },
                formButton(event),
                {
                    loadingText: 'Sending OTP...',
                    target: byId('forgotIdentifier')
                }
            );
        });

        byId('verifyForgotOtp').addEventListener('click', function (event) {
            const group = document.querySelector('[data-otp="forgot"]');
            postApi(
                'api/forgot-verify-otp.php',
                { otp: getOtpValue(group) },
                event.currentTarget,
                {
                    loadingText: 'Verifying OTP...',
                    target: group
                }
            );
        });

        byId('resendForgotOtp').addEventListener('click', function (event) {
            postApi(
                'api/forgot-resend-otp.php',
                {},
                event.currentTarget,
                { loadingText: 'Sending OTP...' }
            );
        });

        byId('resetForgot').addEventListener('click', function (event) {
            postApi(
                'api/reset-forgot.php',
                {},
                event.currentTarget,
                { overlay: false }
            );
        });

        byId('forgotPasswordForm').addEventListener('submit', function (event) {
            event.preventDefault();
            postApi(
                'api/forgot-update-password.php',
                {
                    new_password: byId('forgotNewPassword').value,
                    confirm_password: byId('forgotConfirmPassword').value
                },
                formButton(event),
                {
                    loadingText: 'Saving password...',
                    target: byId('forgotNewPassword')
                }
            );
        });

        byId('resetForgotPassword').addEventListener('click', function (event) {
            postApi(
                'api/reset-forgot.php',
                {},
                event.currentTarget,
                { overlay: false }
            );
        });

        byId('registerContactForm').addEventListener('submit', function (event) {
            event.preventDefault();
            postApi(
                'api/create-send-otp.php',
                {
                    mobile: byId('registerMobile').value,
                    email: byId('registerEmail').value
                },
                formButton(event),
                {
                    loadingText: 'Sending OTP...',
                    target: byId('registerMobile')
                }
            );
        });

        byId('verifyRegisterOtp').addEventListener('click', function (event) {
            const group = document.querySelector('[data-otp="register"]');
            postApi(
                'api/create-verify-otp.php',
                { otp: getOtpValue(group) },
                event.currentTarget,
                {
                    loadingText: 'Verifying OTP...',
                    target: group
                }
            );
        });

        byId('resendRegisterOtp').addEventListener('click', function (event) {
            postApi(
                'create-resend-otp',
                {},
                event.currentTarget,
                { loadingText: 'Sending OTP...' }
            );
        });

        function resetRegistration(event) {
            postApi(
                'api/reset-registration.php',
                {},
                event.currentTarget,
                { overlay: false }
            );
        }

        byId('resetRegisterOtp').addEventListener('click', resetRegistration);
        byId('resetRegister').addEventListener('click', resetRegistration);

        byId('registerDetailsForm').addEventListener('submit', function (event) {
            event.preventDefault();
            postApi(
                'api/create-account.php',
                {
                    name: byId('fullName').value,
                    usertype: byId('userType').value,
                    password: byId('registerPassword').value,
                    confirm_password: byId('confirmPassword').value,
                    terms: byId('terms').checked ? 1 : 0
                },
                formButton(event),
                {
                    loadingText: 'Creating account...',
                    target: byId('fullName')
                }
            );
        });

        document.querySelectorAll('.password-toggle').forEach(function (button) {
            button.addEventListener('click', function () {
                const input = byId(button.dataset.target);
                const showPassword = input.type === 'password';
                input.type = showPassword ? 'text' : 'password';
                button.querySelector('i').className = showPassword
                    ? 'fa-solid fa-eye-slash'
                    : 'fa-solid fa-eye';
                button.setAttribute('aria-label', showPassword ? 'Hide password' : 'Show password');
            });
        });

        byId('registerPassword').addEventListener('input', function () {
            const value = byId('registerPassword').value;
            let score = 0;

            if (value.length >= 8) {
                score += 1;
            }
            if (/[A-Za-z]/.test(value) && /\d/.test(value)) {
                score += 1;
            }
            if (/[^A-Za-z0-9]/.test(value)) {
                score += 1;
            }

            const meter = byId('passwordMeter');
            meter.style.width = (score / 3 * 100) + '%';
            meter.style.background = score < 2
                ? '#b33a3a'
                : score < 3
                    ? '#ad8749'
                    : '#237a57';
        });

        document.querySelectorAll('[inputmode="numeric"]').forEach(function (input) {
            input.addEventListener('input', function () {
                input.value = input.value
                    .replace(/\D/g, '')
                    .slice(0, input.maxLength || 10);
            });
        });

        document.querySelectorAll('.brand-img').forEach(function (image) {
            const showFallback = function () {
                image.hidden = true;
                if (image.nextElementSibling) {
                    image.nextElementSibling.hidden = false;
                }
            };

            image.addEventListener('error', showFallback, { once: true });
            if (image.complete && image.naturalWidth === 0) {
                showFallback();
            }
        });

        switchAuthTab('login', false);
        updateSteps('forgotStepper', 1);
        updateSteps('registerStepper', 1);

        if (FRONTEND_STATE.login_stage === 'otp') {
            showLoginOtpStep({ expires_in: 600, resend_in: 0 });
        }
        if (FRONTEND_STATE.forgot_stage === 'otp') {
            showForgotOtpStep({ expires_in: 600, resend_in: 0 });
        }
        if (FRONTEND_STATE.forgot_stage === 'password') {
            showForgotPasswordStep();
        }
        if (FRONTEND_STATE.register_stage === 'otp') {
            showRegistrationOtpStep({ expires_in: 600, resend_in: 0 });
        }
        if (FRONTEND_STATE.register_stage === 'details') {
            showRegistrationFormStep();
        }
    </script>
</body>
</html>