<?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\UploadService;

$admin = Auth::requireUser();
if (!Auth::hasPermission('users.manage', $admin) && !Auth::hasPermission('users.delete', $admin)) {
    Response::error('You do not have permission to manage system users.', 403);
}
$pdo = Database::connection();
$input = request_input();
$method = $_SERVER['REQUEST_METHOD'];
$action = $input['action'] ?? 'list';

function pyrk_require_users_permission(array $admin, string $permission): void
{
    if (!Auth::hasPermission($permission, $admin)) {
        Response::error('You do not have permission for this user action.', 403);
    }
}

function pyrk_staff_role_by_id(PDO $pdo, int $roleId): ?array
{
    $stmt = $pdo->prepare('SELECT id, name FROM roles WHERE id = ? AND name <> "member" LIMIT 1');
    $stmt->execute([$roleId]);
    $role = $stmt->fetch(PDO::FETCH_ASSOC);
    return $role ?: null;
}

function pyrk_generate_staff_qr_token(PDO $pdo): string
{
    do {
        $token = 'PYRK-USER-' . strtoupper(bin2hex(random_bytes(8)));
        $stmt = $pdo->prepare('SELECT id FROM users WHERE qr_token = ? LIMIT 1');
        $stmt->execute([$token]);
    } while ($stmt->fetch());
    return $token;
}

function pyrk_avatar_from_request(): string
{
    if (empty($_FILES['profile_photo']) || (int)($_FILES['profile_photo']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) {
        return '';
    }
    return (new UploadService())->storeAvatar($_FILES['profile_photo']);
}

function pyrk_can_edit_own_profile_from_input(array $input, int $default = 1): int
{
    if (!array_key_exists('can_edit_own_profile', $input)) return $default === 1 ? 1 : 0;
    return in_array(strtolower(trim((string)$input['can_edit_own_profile'])), ['1', 'true', 'yes', 'on'], true) ? 1 : 0;
}

function pyrk_permission_catalog_for_api(): array
{
    $catalog = [];
    foreach (Auth::permissionCatalog() as $groupKey => $group) {
        $items = [];
        foreach (($group['items'] ?? []) as $key => $label) {
            $items[] = ['key' => $key, 'label' => $label];
        }
        $catalog[] = [
            'key' => $groupKey,
            'label' => $group['label'] ?? $groupKey,
            'items' => $items,
        ];
    }
    return $catalog;
}

function pyrk_permission_table_ready(PDO $pdo): bool
{
    try {
        $pdo->query('SELECT 1 FROM user_permissions LIMIT 1');
        return true;
    } catch (Throwable) {
        return false;
    }
}

function pyrk_selected_permission_keys(PDO $pdo, int $userId, string $roleName): array
{
    if (!pyrk_permission_table_ready($pdo)) {
        return Auth::defaultPermissionKeysForRole($roleName);
    }

    $countStmt = $pdo->prepare('SELECT COUNT(*) FROM user_permissions WHERE user_id = ?');
    $countStmt->execute([$userId]);
    $explicitCount = (int)$countStmt->fetchColumn();

    if ($explicitCount <= 0) {
        return Auth::defaultPermissionKeysForRole($roleName);
    }

    $stmt = $pdo->prepare('SELECT permission_key FROM user_permissions WHERE user_id = ? AND granted = 1 ORDER BY permission_key ASC');
    $stmt->execute([$userId]);
    return array_values(array_map('strval', $stmt->fetchAll(PDO::FETCH_COLUMN)));
}

function pyrk_replace_user_permissions(PDO $pdo, int $userId, array $permissions): void
{
    if (!pyrk_permission_table_ready($pdo)) {
        throw new RuntimeException('Permissions table is missing. Please run database/migrations/012_user_permissions.sql.');
    }

    $allowed = Auth::allPermissionKeys();
    $selected = array_values(array_unique(array_filter(array_map('strval', $permissions), static fn($p) => in_array($p, $allowed, true))));
    $selectedMap = array_flip($selected);

    // Important: store every permission with granted=1/0.
    // This fixes the old bug where clearing all permissions removed all rows,
    // then Auth fell back to role defaults and permissions appeared not to change.
    $pdo->prepare('DELETE FROM user_permissions WHERE user_id = ?')->execute([$userId]);

    $stmt = $pdo->prepare('INSERT INTO user_permissions (user_id, permission_key, granted, created_at, updated_at) VALUES (?, ?, ?, NOW(), NOW())');
    foreach ($allowed as $permission) {
        $stmt->execute([$userId, $permission, isset($selectedMap[$permission]) ? 1 : 0]);
    }
}

function pyrk_permissions_from_input(array $input): ?array
{
    if (!array_key_exists('permissions', $input)) {
        return null;
    }

    $permissions = $input['permissions'];
    if (is_string($permissions)) {
        $decoded = json_decode($permissions, true);
        if (is_array($decoded)) {
            $permissions = $decoded;
        } else {
            $permissions = array_filter(array_map('trim', explode(',', $permissions)));
        }
    }

    if (!is_array($permissions)) {
        return [];
    }

    return $permissions;
}


function pyrk_split_person_name(?string $fullName): array
{
    $fullName = trim(preg_replace('/\s+/', ' ', (string)$fullName));
    if ($fullName === '') {
        return ['first_name' => '', 'last_name' => '', 'full_name' => ''];
    }

    $parts = preg_split('/\s+/', $fullName);
    $first = array_shift($parts) ?: '';
    $last = trim(implode(' ', $parts));

    return [
        'first_name' => $first,
        'last_name' => $last,
        'full_name' => trim($first . ' ' . $last),
    ];
}

function pyrk_person_name_from_input(array $input): array
{
    $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 !== '') {
        $split = pyrk_split_person_name($full);
        $first = $split['first_name'];
        $last = $split['last_name'];
    }

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

    return [
        'first_name' => $first,
        'last_name' => $last,
        'full_name' => trim($first . ' ' . $last),
    ];
}

function pyrk_attach_person_name_parts(?array $row): ?array
{
    if (!$row) return $row;
    $split = pyrk_split_person_name($row['full_name'] ?? '');
    $row['first_name'] = $row['first_name'] ?? $split['first_name'];
    $row['last_name'] = $row['last_name'] ?? $split['last_name'];
    $row['display_name'] = trim(($row['first_name'] ?? '') . ' ' . ($row['last_name'] ?? '')) ?: ($row['full_name'] ?? '');
    return $row;
}


function pyrk_staff_select_sql(): string
{
    return 'SELECT u.id, u.role_id, r.name AS role_name, u.full_name, u.email, u.mobile, u.profile_photo_path, u.can_edit_own_profile, u.qr_token, u.status, u.failed_login_count, u.locked_until, u.last_login_at, u.last_login_ip, u.blocked_at, u.blocked_reason, u.created_at, u.updated_at, COALESCE(mp.total_walks,0) AS total_walks, COALESCE(mp.total_kilometers,0) AS total_kilometers, COALESCE(mp.loyalty_points,0) AS loyalty_points, (SELECT COUNT(*) FROM user_badges ub WHERE ub.user_id = u.id) AS badges_count FROM users u JOIN roles r ON r.id = u.role_id LEFT JOIN member_profiles mp ON mp.user_id = u.id WHERE r.name <> "member"';
}

if ($method === 'GET' && $action === 'roles') {
    $roles = $pdo->query('SELECT id, name FROM roles WHERE name <> "member" ORDER BY id')->fetchAll(PDO::FETCH_ASSOC);
    Response::ok(['roles' => $roles, 'permissions_catalog' => pyrk_permission_catalog_for_api()]);
}

if ($method === 'GET' && $action === 'list') {
    $stmt = $pdo->query(pyrk_staff_select_sql() . ' ORDER BY FIELD(r.name,"super_admin","event_manager","walk_leader"), u.full_name ASC');
    $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
    foreach ($users as &$row) {
        $row = pyrk_attach_person_name_parts($row);
        $row['permissions'] = pyrk_selected_permission_keys($pdo, (int)$row['id'], (string)$row['role_name']);
        $row['permissions_count'] = count($row['permissions']);
    }
    unset($row);
    $roles = $pdo->query('SELECT id, name FROM roles WHERE name <> "member" ORDER BY id')->fetchAll(PDO::FETCH_ASSOC);
    Response::ok([
        'users' => $users,
        'roles' => $roles,
        'permissions_catalog' => pyrk_permission_catalog_for_api(),
    ]);
}

if ($method === 'GET' && $action === 'detail') {
    $id = (int)($input['id'] ?? 0);
    if (!$id) Response::error('Missing user id.');

    $stmt = $pdo->prepare(pyrk_staff_select_sql() . ' AND u.id = ? LIMIT 1');
    $stmt->execute([$id]);
    $staff = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$staff) Response::error('User not found.', 404);

    $staff['permissions'] = pyrk_selected_permission_keys($pdo, (int)$staff['id'], (string)$staff['role_name']);

    $loginLogs = [];
    try {
        $logs = $pdo->prepare('SELECT success, failure_reason, ip_address, user_agent, login_at, logout_at, logout_reason FROM login_logs WHERE user_id = ? ORDER BY login_at DESC, id DESC LIMIT 30');
        $logs->execute([$id]);
        $loginLogs = $logs->fetchAll(PDO::FETCH_ASSOC);
    } catch (Throwable) {
        $loginLogs = [];
    }

    Response::ok([
        'user' => $staff,
        'login_logs' => $loginLogs,
        'permissions_catalog' => pyrk_permission_catalog_for_api(),
    ]);
}

if ($method === 'POST' && $action === 'create') {
    pyrk_require_users_permission($admin, 'users.manage');
    Csrf::validateFromRequest();

    foreach (['email','role_id'] as $field) {
        if (trim((string)($input[$field] ?? '')) === '') Response::error("Missing field: {$field}");
    }
    $personName = pyrk_person_name_from_input($input);

    $roleId = (int)$input['role_id'];
    $role = pyrk_staff_role_by_id($pdo, $roleId);
    if (!$role) Response::error('Invalid staff role. Use Members page for club members.');

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

    $password = (string)($input['password'] ?? '');
    if ($password === '') {
        Response::error('Password is required for new users.');
    }
    if (strlen($password) < 8) {
        Response::error('Password must be at least 8 characters.');
    }

    $avatar = pyrk_avatar_from_request();
    $permissions = pyrk_permissions_from_input($input);
    if ($permissions === null) {
        $permissions = Auth::defaultPermissionKeysForRole((string)$role['name']);
    }

    try {
        $pdo->beginTransaction();

        $canEditOwnProfile = pyrk_can_edit_own_profile_from_input($input, 1);
        $stmt = $pdo->prepare('INSERT INTO users (role_id, full_name, email, mobile, profile_photo_path, can_edit_own_profile, qr_token, password_hash, status, email_verified_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), NOW())');
        $stmt->execute([
            $roleId,
            $personName['full_name'],
            $email,
            trim((string)($input['mobile'] ?? '')),
            $avatar ?: null,
            $canEditOwnProfile,
            pyrk_generate_staff_qr_token($pdo),
            password_hash($password, PASSWORD_DEFAULT),
            in_array(($input['status'] ?? 'active'), ['active','inactive','blocked'], true) ? $input['status'] : 'active',
        ]);
        $id = (int)$pdo->lastInsertId();

        pyrk_replace_user_permissions($pdo, $id, $permissions);

        Security::audit((int)$admin['id'], 'staff_user_created', 'users', $id, ['email' => $email, 'role' => $role['name'], 'permissions' => $permissions, 'can_edit_own_profile' => $canEditOwnProfile]);
        $pdo->commit();
        Response::ok(['user_id' => $id], 'System user created.');
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        if (str_contains($e->getMessage(), 'Duplicate')) Response::error('This email already exists.');
        Response::error('Could not create user: ' . $e->getMessage());
    }
}

if ($method === 'POST' && $action === 'update') {
    pyrk_require_users_permission($admin, 'users.manage');
    Csrf::validateFromRequest();

    $id = (int)($input['id'] ?? 0);
    if (!$id) Response::error('Missing user id.');

    foreach (['email','role_id'] as $field) {
        if (trim((string)($input[$field] ?? '')) === '') Response::error("Missing field: {$field}");
    }
    $personName = pyrk_person_name_from_input($input);

    $roleId = (int)$input['role_id'];
    $role = pyrk_staff_role_by_id($pdo, $roleId);
    if (!$role) Response::error('Invalid staff role.');

    $existing = $pdo->prepare('SELECT u.id, u.can_edit_own_profile FROM users u JOIN roles r ON r.id = u.role_id WHERE u.id = ? AND r.name <> "member" LIMIT 1');
    $existing->execute([$id]);
    $existingUser = $existing->fetch(PDO::FETCH_ASSOC);
    if (!$existingUser) Response::error('Staff user not found.', 404);

    $email = strtolower(trim((string)$input['email']));
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) Response::error('Invalid email address.');
    if (!empty($input['password']) && strlen((string)$input['password']) < 8) Response::error('Password must be at least 8 characters.');

    $avatar = pyrk_avatar_from_request();
    $permissions = pyrk_permissions_from_input($input);

    try {
        $pdo->beginTransaction();

        $canEditOwnProfile = pyrk_can_edit_own_profile_from_input($input, (int)($existingUser['can_edit_own_profile'] ?? 1));
        $stmt = $pdo->prepare('UPDATE users SET role_id = ?, full_name = ?, email = ?, mobile = ?, status = ?, can_edit_own_profile = ?, updated_at = NOW() WHERE id = ?');
        $stmt->execute([
            $roleId,
            $personName['full_name'],
            $email,
            trim((string)($input['mobile'] ?? '')),
            in_array(($input['status'] ?? 'active'), ['active','inactive','blocked'], true) ? $input['status'] : 'active',
            $canEditOwnProfile,
            $id
        ]);

        if ($avatar) {
            $pdo->prepare('UPDATE users SET profile_photo_path = ?, updated_at = NOW() WHERE id = ?')->execute([$avatar, $id]);
        }

        if (!empty($input['password'])) {
            $pdo->prepare('UPDATE users SET password_hash = ?, updated_at = NOW() WHERE id = ?')->execute([password_hash((string)$input['password'], PASSWORD_DEFAULT), $id]);
        }

        if ($permissions !== null) {
            pyrk_replace_user_permissions($pdo, $id, $permissions);
        }

        Security::audit((int)$admin['id'], 'staff_user_updated', 'users', $id, ['email' => $email, 'role' => $role['name'], 'permissions' => $permissions, 'can_edit_own_profile' => $canEditOwnProfile]);
        $pdo->commit();
        Response::ok(['user_id' => $id], 'System user updated.');
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        if (str_contains($e->getMessage(), 'Duplicate')) Response::error('This email already exists.');
        Response::error('Could not update user: ' . $e->getMessage());
    }
}

if ($method === 'POST' && $action === 'save_permissions') {
    pyrk_require_users_permission($admin, 'users.manage');
    Csrf::validateFromRequest();

    $id = (int)($input['id'] ?? $input['user_id'] ?? 0);
    if (!$id) Response::error('Missing user id.');

    $stmt = $pdo->prepare('SELECT u.id, r.name AS role_name FROM users u JOIN roles r ON r.id = u.role_id WHERE u.id = ? AND r.name <> "member" LIMIT 1');
    $stmt->execute([$id]);
    $staff = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$staff) Response::error('System user not found.', 404);

    $permissions = pyrk_permissions_from_input($input) ?? [];
    try {
        pyrk_replace_user_permissions($pdo, $id, $permissions);
        Security::audit((int)$admin['id'], 'staff_permissions_updated', 'users', $id, ['permissions' => $permissions]);
        Response::ok([], 'Permissions updated.');
    } catch (Throwable $e) {
        Response::error('Could not save permissions: ' . $e->getMessage());
    }
}

if ($method === 'POST' && $action === 'block') {
    pyrk_require_users_permission($admin, 'users.manage');
    Csrf::validateFromRequest();
    $id = (int)($input['id'] ?? $input['user_id'] ?? 0);
    $reason = trim((string)($input['reason'] ?? 'Blocked by admin'));
    if (!$id) Response::error('Missing user id.');
    if ($id === (int)$admin['id']) Response::error('You cannot block your own account.');

    $existing = $pdo->prepare('SELECT u.* FROM users u JOIN roles r ON r.id = u.role_id WHERE u.id = ? AND r.name <> "member" LIMIT 1');
    $existing->execute([$id]);
    $before = $existing->fetch(PDO::FETCH_ASSOC);
    if (!$before) Response::error('System user not found.', 404);

    $pdo->prepare('UPDATE users SET status = "blocked", locked_until = NULL, failed_login_count = 0, blocked_at = NOW(), blocked_by = ?, blocked_reason = ?, updated_at = NOW() WHERE id = ?')->execute([(int)$admin['id'], $reason, $id]);
    Security::audit((int)$admin['id'], 'staff_user_blocked', 'users', $id, ['reason' => $reason], $before);
    Response::ok([], 'System user blocked.');
}

if ($method === 'POST' && $action === 'unblock') {
    pyrk_require_users_permission($admin, 'users.manage');
    Csrf::validateFromRequest();
    $id = (int)($input['id'] ?? $input['user_id'] ?? 0);
    if (!$id) Response::error('Missing user id.');

    $existing = $pdo->prepare('SELECT u.* FROM users u JOIN roles r ON r.id = u.role_id WHERE u.id = ? AND r.name <> "member" LIMIT 1');
    $existing->execute([$id]);
    $before = $existing->fetch(PDO::FETCH_ASSOC);
    if (!$before) Response::error('System user not found.', 404);

    $pdo->prepare('UPDATE users SET status = "active", locked_until = NULL, failed_login_count = 0, blocked_at = NULL, blocked_by = NULL, blocked_reason = NULL, updated_at = NOW() WHERE id = ?')->execute([$id]);
    Security::audit((int)$admin['id'], 'staff_user_unblocked', 'users', $id, null, $before);
    Response::ok([], 'System user unblocked.');
}

if ($method === 'POST' && $action === 'reset_lock') {
    pyrk_require_users_permission($admin, 'users.manage');
    Csrf::validateFromRequest();
    $id = (int)($input['id'] ?? $input['user_id'] ?? 0);
    if (!$id) Response::error('Missing user id.');

    $pdo->prepare('UPDATE users SET locked_until = NULL, failed_login_count = 0, updated_at = NOW() WHERE id = ?')->execute([$id]);
    Security::audit((int)$admin['id'], 'staff_user_lock_reset', 'users', $id);
    Response::ok([], 'System user login lock reset.');
}


if ($method === 'POST' && $action === 'delete') {
    pyrk_require_users_permission($admin, 'users.delete');
    Csrf::validateFromRequest();

    $id = (int)($input['id'] ?? $input['user_id'] ?? 0);
    if (!$id) Response::error('Missing user id.');
    if ($id === (int)$admin['id']) Response::error('You cannot delete your own account.');

    $stmt = $pdo->prepare('SELECT u.*, r.name AS role_name FROM users u JOIN roles r ON r.id = u.role_id WHERE u.id = ? AND r.name <> "member" LIMIT 1');
    $stmt->execute([$id]);
    $before = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$before) Response::error('System user not found.', 404);
    if (($before['role_name'] ?? '') === 'super_admin') {
        $count = (int)$pdo->query('SELECT COUNT(*) FROM users u JOIN roles r ON r.id = u.role_id WHERE r.name = "super_admin" AND u.status = "active"')->fetchColumn();
        if ($count <= 1) Response::error('You cannot delete the last active Super Admin.');
    }

    try {
        $pdo->beginTransaction();
        $pdo->prepare('DELETE FROM user_permissions WHERE user_id = ?')->execute([$id]);
        $pdo->prepare('DELETE FROM users WHERE id = ?')->execute([$id]);
        Security::audit((int)$admin['id'], 'staff_user_deleted', 'users', $id, ['email' => $before['email'] ?? null, 'role' => $before['role_name'] ?? null], $before);
        $pdo->commit();
        Response::ok([], 'System user deleted.');
    } catch (Throwable $e) {
        if ($pdo->inTransaction()) $pdo->rollBack();
        Response::error('Could not delete system user: ' . $e->getMessage());
    }
}

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