<?php
require __DIR__ . '/bootstrap.php';

use App\Core\Auth;
use App\Core\Csrf;
use App\Core\Database;
use App\Core\Response;
use App\Core\Security;
use App\Services\NotificationService;

$method = $_SERVER['REQUEST_METHOD'];
$input = request_input();
$action = $input['action'] ?? '';

function pyrk_login_redirect_url(?array $user): string
{
    $role = $user['role_name'] ?? '';
    return in_array($role, ['super_admin', 'event_manager', 'walk_leader'], true)
        ? 'admin/dashboard.php'
        : (Auth::isNativeAppRequest() ? 'mobile_home.php' : 'index.php');
}

function pyrk_normalize_dmy_date(?string $value): ?string
{
    $value = trim((string)$value);
    if ($value === '') return null;

    if (preg_match('/^(\d{2})\/(\d{2})\/(\d{4})$/', $value, $m)) {
        if (!checkdate((int)$m[2], (int)$m[1], (int)$m[3])) {
            Response::error('Date of birth must be a valid date in dd/mm/yyyy format.');
        }
        return $m[3] . '-' . $m[2] . '-' . $m[1];
    }

    if (preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $value, $m)) {
        if (!checkdate((int)$m[2], (int)$m[3], (int)$m[1])) {
            Response::error('Date of birth must be valid.');
        }
        return $value;
    }

    Response::error('Date of birth must be dd/mm/yyyy.');
}

function pyrk_normalize_phone(array $input, string $fullKey, string $countryKey, string $localKey, string $label): string
{
    $full = trim((string)($input[$fullKey] ?? ''));

    if ($full !== '') {
        $value = '+' . preg_replace('/\D+/', '', $full);
        if (str_starts_with($full, '+')) {
            $value = '+' . preg_replace('/\D+/', '', substr($full, 1));
        }
        if (preg_match('/^\+\d{7,15}$/', $value)) return $value;
    }

    $country = trim((string)($input[$countryKey] ?? ''));
    $local = preg_replace('/\D+/', '', (string)($input[$localKey] ?? ''));
    $local = ltrim($local, '0');

    if ($country === '' || $local === '') {
        Response::error($label . ' is required.');
    }

    $countryDigits = preg_replace('/\D+/', '', $country);
    $value = '+' . $countryDigits . $local;

    if (!preg_match('/^\+\d{7,15}$/', $value)) {
        Response::error($label . ' must be a valid international mobile number.');
    }

    return $value;
}


function pyrk_registration_full_name(array $input): string
{
    $first = trim(preg_replace('/\s+/', ' ', (string)($input['first_name'] ?? '')));
    $last = trim(preg_replace('/\s+/', ' ', (string)($input['last_name'] ?? '')));
    $full = trim(preg_replace('/\s+/', ' ', (string)($input['full_name'] ?? '')));

    if ($first === '' && $last === '' && $full !== '') {
        $parts = preg_split('/\s+/', $full);
        $first = array_shift($parts) ?: '';
        $last = trim(implode(' ', $parts));
    }

    if ($first === '') Response::error('First Name is required.');
    if ($last === '') Response::error('Last Name is required.');

    return trim($first . ' ' . $last);
}


function pyrk_public_activation_url(string $token): string
{
    $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
    $scheme = $https ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $scriptDir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '/api/auth.php'));
    $projectRoot = rtrim(dirname($scriptDir), '/');
    if ($projectRoot === '/' || $projectRoot === '.') $projectRoot = '';
    return $scheme . '://' . $host . $projectRoot . '/public/activate.php?token=' . rawurlencode($token);
}


function pyrk_activation_link_lifetime_seconds(): int
{
    return 2 * 60 * 60;
}


function pyrk_member_activation_required(): bool
{
    try {
        $pdo = Database::connection();
        $stmt = $pdo->prepare('SELECT setting_value FROM app_settings WHERE setting_key = ? LIMIT 1');
        $stmt->execute(['member_activation_required']);
        $value = $stmt->fetchColumn();
        if ($value === false || $value === null || $value === '') {
            return false;
        }
        return in_array(strtolower((string)$value), ['1','true','yes','on'], true);
    } catch (Throwable) {
        // If settings are unavailable, prefer the simplified member experience requested by admin.
        return false;
    }
}


function pyrk_public_password_reset_url(string $token): string
{
    $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https');
    $scheme = $https ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $scriptDir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '/api/auth.php'));
    $projectRoot = rtrim(dirname($scriptDir), '/');
    if ($projectRoot === '/' || $projectRoot === '.') $projectRoot = '';
    return $scheme . '://' . $host . $projectRoot . '/public/reset_password.php?token=' . rawurlencode($token);
}

function pyrk_password_reset_lifetime_seconds(): int
{
    return 45 * 60;
}

function pyrk_password_reset_public_message(): string
{
    return 'If this email belongs to a member account, a password reset link will be sent. Please check your inbox and spam/junk folder.';
}

function pyrk_request_password_reset_for_member(string $email): void
{
    Csrf::validateFromRequest();

    $email = strtolower(trim($email));
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        Response::error('Please enter a valid email address.');
    }

    $rateKey = 'password_reset|' . hash('sha256', $email . '|' . Security::clientIp());
    $maxRequests = 5;
    $lockMinutes = 15;
    if (Security::tooManyAttempts('request_password_reset', $rateKey, $maxRequests, $lockMinutes)) {
        Response::error('Too many password reset requests. Please wait 15 minutes and try again.', 429);
    }
    Security::recordAttempt('request_password_reset', $rateKey, false, $maxRequests, $lockMinutes);

    $genericMessage = pyrk_password_reset_public_message();
    $pdo = Database::connection();

    try {
        $stmt = $pdo->prepare('SELECT u.id, u.full_name, u.email, u.status, u.password_reset_sent_at, ro.name AS role_name
                               FROM users u
                               JOIN roles ro ON ro.id = u.role_id
                               WHERE u.email = ?
                               LIMIT 1');
        $stmt->execute([$email]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
    } catch (Throwable) {
        Response::error('Password reset is not installed yet. Please run the password reset SQL migration first.', 500);
    }

    if (!$user || ($user['role_name'] ?? '') !== 'member') {
        Response::ok(['sent' => false], $genericMessage);
    }

    if (($user['status'] ?? '') === 'blocked') {
        Response::ok(['sent' => false], $genericMessage);
    }

    if (!empty($user['password_reset_sent_at'])) {
        $lastSent = strtotime((string)$user['password_reset_sent_at']);
        if ($lastSent && $lastSent > time() - 90) {
            Response::ok(
                ['sent' => false, 'cooldown_seconds' => max(1, 90 - (time() - $lastSent))],
                'A password reset email was sent recently. Please wait a minute, then check your inbox and spam/junk folder.'
            );
        }
    }

    $token = bin2hex(random_bytes(32));
    $tokenHash = hash('sha256', $token);
    $expiresAt = date('Y-m-d H:i:s', time() + pyrk_password_reset_lifetime_seconds());

    $pdo->prepare('UPDATE users
                   SET password_reset_token_hash = ?,
                       password_reset_expires_at = ?,
                       password_reset_sent_at = NOW(),
                       updated_at = NOW()
                   WHERE id = ?')
        ->execute([$tokenHash, $expiresAt, (int)$user['id']]);

    $resetUrl = pyrk_public_password_reset_url($token);
    $notifier = new NotificationService();
    $mailResult = $notifier->sendPasswordResetEmail((int)$user['id'], $resetUrl, $expiresAt);
    $mailStatus = (string)($mailResult['status'] ?? 'failed');

    if ($mailStatus === 'sent') {
        Response::ok(['sent' => true, 'password_reset_email_status' => $mailStatus], 'Password reset email sent. Please check your inbox and spam/junk folder.');
    }

    if ($mailStatus === 'logged') {
        Response::ok(['sent' => false, 'password_reset_email_status' => $mailStatus], 'The password reset link was generated, but email delivery is not configured. Please contact the administrator.');
    }

    if ($mailStatus === 'disabled') {
        Response::ok(['sent' => false, 'password_reset_email_status' => $mailStatus], 'Email notifications are currently disabled. Please contact the administrator.');
    }

    Response::error('The password reset email could not be sent. Please contact the administrator.', 500, [
        'password_reset_email_status' => $mailStatus,
        'password_reset_email_error' => $mailResult['error'] ?? null,
    ]);
}

function pyrk_reset_member_password(string $token, string $password, string $confirmPassword = ''): void
{
    Csrf::validateFromRequest();

    $token = trim($token);
    if (!preg_match('/^[a-f0-9]{64}$/i', $token)) {
        Response::error('Password reset link is invalid. Please request a fresh link.');
    }

    if (strlen($password) < 8) {
        Response::error('Password must be at least 8 characters.');
    }

    if ($confirmPassword !== '' && !hash_equals($password, $confirmPassword)) {
        Response::error('Passwords do not match.');
    }

    $pdo = Database::connection();
    $tokenHash = hash('sha256', $token);

    try {
        $stmt = $pdo->prepare('SELECT u.id, u.full_name, u.email, u.status, u.password_reset_expires_at, ro.name AS role_name
                               FROM users u
                               JOIN roles ro ON ro.id = u.role_id
                               WHERE u.password_reset_token_hash = ?
                               LIMIT 1');
        $stmt->execute([$tokenHash]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
    } catch (Throwable) {
        Response::error('Password reset is not installed yet. Please run the password reset SQL migration first.', 500);
    }

    if (!$user || ($user['role_name'] ?? '') !== 'member') {
        Response::error('Password reset link is invalid or has already been used. Please request a fresh link.');
    }

    if (($user['status'] ?? '') === 'blocked') {
        Response::error('This account is blocked. Please contact the administrator.');
    }

    if (empty($user['password_reset_expires_at']) || strtotime((string)$user['password_reset_expires_at']) < time()) {
        $pdo->prepare('UPDATE users SET password_reset_token_hash = NULL, password_reset_expires_at = NULL, updated_at = NOW() WHERE id = ?')
            ->execute([(int)$user['id']]);
        Response::error('Password reset link expired. Please request a fresh link.');
    }

    $pdo->prepare('UPDATE users
                   SET password_hash = ?,
                       password_reset_token_hash = NULL,
                       password_reset_expires_at = NULL,
                       password_reset_sent_at = NULL,
                       failed_login_count = 0,
                       locked_until = NULL,
                       updated_at = NOW()
                   WHERE id = ?')
        ->execute([password_hash($password, PASSWORD_DEFAULT), (int)$user['id']]);

    Security::audit((int)$user['id'], 'member_password_reset', 'users', (int)$user['id']);

    if (($user['status'] ?? '') !== 'active') {
        Response::ok(
            ['password_changed' => true, 'activation_required' => true],
            'Password changed successfully. Your account still needs activation before you can log in.'
        );
    }

    Response::ok(['password_changed' => true], 'Password changed successfully. You can log in now.');
}

function pyrk_activation_resend_public_message(): string
{
    return 'If this email belongs to an account that still needs activation, a new activation link will be sent. Please check your inbox and spam/junk folder.';
}

function pyrk_is_activation_blocked_account(array $user): bool
{
    $status = (string)($user['status'] ?? '');
    $reason = strtolower((string)($user['blocked_reason'] ?? ''));
    return $status === 'blocked' && str_contains($reason, 'activation');
}

function pyrk_resend_activation_email_for_member(string $email): void
{
    Csrf::validateFromRequest();

    $email = strtolower(trim($email));
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        Response::error('Please enter a valid email address.');
    }

    $rateKey = 'activation_resend|' . hash('sha256', $email . '|' . Security::clientIp());
    $maxRequests = 5;
    $lockMinutes = 15;
    if (Security::tooManyAttempts('resend_activation', $rateKey, $maxRequests, $lockMinutes)) {
        Response::error('Too many activation email requests. Please wait 15 minutes and try again.', 429);
    }
    Security::recordAttempt('resend_activation', $rateKey, false, $maxRequests, $lockMinutes);

    $genericMessage = pyrk_activation_resend_public_message();
    $pdo = Database::connection();

    $stmt = $pdo->prepare('SELECT u.id, u.full_name, u.email, u.status, u.email_verified_at, u.activation_sent_at, u.blocked_reason, ro.name AS role_name
                           FROM users u
                           JOIN roles ro ON ro.id = u.role_id
                           WHERE u.email = ?
                           LIMIT 1');
    $stmt->execute([$email]);
    $user = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$user) {
        Response::ok(['sent' => false], $genericMessage);
    }

    if (($user['role_name'] ?? '') !== 'member') {
        Response::ok(['sent' => false], $genericMessage);
    }

    if (!empty($user['email_verified_at']) || ($user['status'] ?? '') === 'active') {
        Response::ok(['sent' => false, 'already_active' => true], 'This account is already active. You can log in now.');
    }

    $status = (string)($user['status'] ?? '');
    $canResend = $status === 'inactive' || pyrk_is_activation_blocked_account($user);
    if (!$canResend) {
        Response::ok(['sent' => false], $genericMessage);
    }

    if (!empty($user['activation_sent_at'])) {
        $lastSent = strtotime((string)$user['activation_sent_at']);
        if ($lastSent && $lastSent > time() - 90) {
            Response::ok(
                ['sent' => false, 'cooldown_seconds' => max(1, 90 - (time() - $lastSent))],
                'An activation email was sent recently. Please wait a minute, then check your inbox and spam/junk folder.'
            );
        }
    }

    $activationRequired = pyrk_member_activation_required();
    $token = $activationRequired ? bin2hex(random_bytes(32)) : null;
    $expiresAt = $activationRequired ? date('Y-m-d H:i:s', time() + pyrk_activation_link_lifetime_seconds()) : null;

    $pdo->prepare('UPDATE users
                   SET status = "inactive",
                       activation_token = ?,
                       activation_expires_at = ?,
                       activation_sent_at = NOW(),
                       email_verified_at = NULL,
                       blocked_at = NULL,
                       blocked_by = NULL,
                       blocked_reason = NULL,
                       failed_login_count = 0,
                       locked_until = NULL,
                       updated_at = NOW()
                   WHERE id = ?')
        ->execute([$token, $expiresAt, (int)$user['id']]);

    $activationUrl = pyrk_public_activation_url($token);
    $notifier = new NotificationService();
    $mailResult = $notifier->sendActivationEmail((int)$user['id'], $activationUrl, $expiresAt);
    $mailStatus = (string)($mailResult['status'] ?? 'failed');

    if ($mailStatus === 'sent') {
        Response::ok(['sent' => true, 'activation_email_status' => $mailStatus], 'A new activation email was sent. Please check your inbox and spam/junk folder.');
    }

    if ($mailStatus === 'logged') {
        Response::ok(['sent' => false, 'activation_email_status' => $mailStatus], 'The activation link was generated, but email delivery is not configured. Please contact the administrator.');
    }

    if ($mailStatus === 'disabled') {
        Response::ok(['sent' => false, 'activation_email_status' => $mailStatus], 'Email notifications are currently disabled. Please contact the administrator.');
    }

    Response::error('The activation email could not be sent. Please contact the administrator.', 500, [
        'activation_email_status' => $mailStatus,
        'activation_email_error' => $mailResult['error'] ?? null,
    ]);
}


function pyrk_signup_code_clean(?string $code): string
{
    $code = strtoupper(trim((string)$code));
    $code = preg_replace('/[^A-Z0-9_-]+/', '', $code);
    return substr($code, 0, 64);
}

function pyrk_find_signup_group(PDO $pdo, string $code): ?array
{
    if ($code === '') return null;

    try {
        $stmt = $pdo->prepare('SELECT id, group_name, status, signup_code_enabled FROM member_groups WHERE signup_code = ? LIMIT 1');
        $stmt->execute([$code]);
        $group = $stmt->fetch(PDO::FETCH_ASSOC);
    } catch (Throwable) {
        Response::error('Group signup code is not configured yet. Please run the group signup code SQL migration.');
    }

    if (!$group) {
        Response::error('Invalid group code. Please check the code and try again.');
    }

    if (($group['status'] ?? '') !== 'active' || (int)($group['signup_code_enabled'] ?? 0) !== 1) {
        Response::error('This group code is not active anymore. Please contact the organizer.');
    }

    return $group;
}


if ($method === 'GET' && $action === 'me') {
    $user = Auth::user();
    Response::ok(['user' => $user]);
}

if ($method === 'POST' && $action === 'login') {
    Csrf::validateFromRequest();

    if (Auth::attempt(trim($input['email'] ?? ''), (string)($input['password'] ?? ''))) {
        $user = Auth::user();
        Response::ok(['user' => $user, 'redirect_url' => pyrk_login_redirect_url($user)], 'Logged in.');
    }

    Response::error('Invalid email or password. If you just registered, please activate your account from the email link first.', 401);
}

if ($method === 'POST' && $action === 'resend_activation') {
    pyrk_resend_activation_email_for_member((string)($input['email'] ?? ''));
}

if ($method === 'POST' && $action === 'request_password_reset') {
    pyrk_request_password_reset_for_member((string)($input['email'] ?? ''));
}

if ($method === 'POST' && $action === 'reset_password') {
    pyrk_reset_member_password((string)($input['token'] ?? ''), (string)($input['password'] ?? ''), (string)($input['password_confirm'] ?? ''));
}

if ($method === 'POST' && $action === 'register') {
    Csrf::validateFromRequest();

    $required = ['first_name','last_name','email','password','date_of_birth','gender'];
    foreach ($required as $field) {
        if (empty($input[$field])) Response::error("Missing field: {$field}");
    }

    $fullName = pyrk_registration_full_name($input);
    $email = strtolower(trim((string)$input['email']));
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::error('Invalid email address.');

    $password = (string)($input['password'] ?? '');
    if (strlen($password) < 8) Response::error('Password must be at least 8 characters.');

    $mobile = pyrk_normalize_phone($input, 'mobile', 'mobile_country_code', 'mobile_local', 'Mobile number');
    $emergencyMobile = pyrk_normalize_phone($input, 'emergency_contact_mobile', 'emergency_contact_country_code', 'emergency_contact_mobile_local', 'Emergency contact number');
    $dob = pyrk_normalize_dmy_date((string)$input['date_of_birth']);
    $signupCode = pyrk_signup_code_clean($input['signup_code'] ?? '');
    $signupGroup = null;

    $activationRequired = pyrk_member_activation_required();
    $token = $activationRequired ? bin2hex(random_bytes(32)) : null;
    $expiresAt = $activationRequired ? date('Y-m-d H:i:s', time() + pyrk_activation_link_lifetime_seconds()) : null;

    $pdo = Database::connection();
    $pdo->beginTransaction();

    try {
        $roleId = (int)$pdo->query('SELECT id FROM roles WHERE name = "member" LIMIT 1')->fetchColumn();
        if (!$roleId) Response::error('Member role is missing.');

        if ($signupCode !== '') {
            $signupGroup = pyrk_find_signup_group($pdo, $signupCode);
        }

        $stmt = $pdo->prepare('INSERT INTO users (role_id, full_name, email, mobile, qr_token, password_hash, status, email_verified_at, activation_token, activation_expires_at, activation_sent_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())');
        $qrToken = 'PYRK-USER-' . strtoupper(bin2hex(random_bytes(8)));

        $stmt->execute([
            $roleId,
            $fullName,
            $email,
            $mobile,
            $qrToken,
            password_hash($password, PASSWORD_DEFAULT),
            $activationRequired ? 'inactive' : 'active',
            $activationRequired ? null : date('Y-m-d H:i:s'),
            $token,
            $expiresAt,
            $activationRequired ? date('Y-m-d H:i:s') : null
        ]);

        $userId = (int)$pdo->lastInsertId();

        $profile = $pdo->prepare('INSERT INTO member_profiles (user_id, date_of_birth, gender, emergency_contact_name, emergency_contact_mobile, medical_conditions, photo_consent, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())');
        $profile->execute([
            $userId,
            $dob,
            $input['gender'],
            $input['emergency_contact_name'] ?? '',
            $emergencyMobile,
            $input['medical_conditions'] ?? '',
            !empty($input['photo_consent']) ? 1 : 0
        ]);

        if ($signupGroup) {
            $membership = $pdo->prepare('INSERT IGNORE INTO member_group_members (group_id, user_id, created_by, created_at) VALUES (?, ?, NULL, NOW())');
            $membership->execute([(int)$signupGroup['id'], $userId]);
        }

        $pdo->commit();

        $notifier = new NotificationService();
        $notifier->notifyMemberRegistered($userId);

        if (!$activationRequired) {
            // Admin requested no activation link when members already create username/email + password.
            // The account is active immediately and the user is logged in.
            Auth::attempt($email, $password);
            $loggedUser = Auth::user();
            $message = 'Account created successfully. You can use your email and password immediately.';
            Response::ok(
                [
                    'activation_required' => false,
                    'email' => $email,
                    'assigned_group' => $signupGroup ? $signupGroup['group_name'] : null,
                    'user' => $loggedUser,
                    'redirect_url' => pyrk_login_redirect_url($loggedUser)
                ],
                $signupGroup ? ($message . ' You were assigned to group: ' . $signupGroup['group_name'] . '.') : $message
            );
        }

        $activationUrl = pyrk_public_activation_url((string)$token);
        $mailResult = $notifier->sendActivationEmail($userId, $activationUrl, (string)$expiresAt);
        $mailStatus = (string)($mailResult['status'] ?? 'failed');

        $message = 'Registration received.';
        if ($mailStatus === 'sent') {
            $message .= ' We sent an activation email. Please activate your account within 2 hours.';
        } elseif ($mailStatus === 'logged') {
            $message .= ' Activation email was logged but not sent because mail delivery is not configured.';
        } elseif ($mailStatus === 'disabled') {
            $message .= ' Activation email was not sent because email notifications are disabled.';
        } else {
            $message .= ' Activation email could not be sent. Please contact the admin.';
        }

        Response::ok(
            [
                'activation_required' => true,
                'email' => $email,
                'activation_email_status' => $mailStatus,
                'activation_email_error' => $mailResult['error'] ?? null,
                'assigned_group' => $signupGroup ? $signupGroup['group_name'] : null
            ],
            $signupGroup ? ($message . ' You were assigned to group: ' . $signupGroup['group_name'] . '.') : $message
        );
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        if (str_contains($e->getMessage(), 'Duplicate')) Response::error('This email already exists.');
        Response::error('Registration failed: ' . $e->getMessage());
    }
}

if ($method === 'POST' && $action === 'logout') {
    Csrf::validateFromRequest();
    Auth::logout();
    Response::ok([], 'Logged out.');
}

Response::error('Unsupported auth action.', 404);
