not loading exercises on the exercise screen.
Backend:
GET /api/exercises called
Fetched 0 exercises for user 66fa12b2e515368eb51e150e
GET /api/exercises called
Fetched 0 exercises for user 66fa12b2e515368eb51e150e
GET /api/exercises called
Fetched 0 exercises for user 66fa12b2e515368eb51e150e
GET /api/exercises called
Fetched 0 exercises for user 66fa12b2e515368eb51e150e
GET /api/exercises called
Fetched 0 exercises for user 66fa12b2e515368eb51e150e


frontend:
 LOG  Token stored successfully.
 LOG  Loaded exercises from SecureStore.
 LOG  Fetching exercises with token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NmZhMTJiMmU1MTUzNjhlYjUxZTE1MGUiLCJpYXQiOjE3Mjc4OTE0MDcsImV4cCI6MTcyNzg5NTAwN30.F5749zTA86MmSvPWaPr7uarldmnWvS_QbdRC3CJV8fg
 LOG  Fetched exercises from API and cached.

// screens/ExercisesScreen.js

import React, { useEffect, useState, useContext, useCallback } from 'react';
import {
    View,
    Text,
    FlatList,
    Image,
    StyleSheet,
    ActivityIndicator,
    Alert,
    TouchableOpacity,
    Linking,
    TextInput,
    RefreshControl,
} from 'react-native';
import axios from 'axios';
import { AuthContext } from '../context/AuthContext';
import debounce from 'lodash.debounce';
import * as SecureStore from 'expo-secure-store';

const ExercisesScreen = () => {
    const { authToken } = useContext(AuthContext);
    const [exercises, setExercises] = useState([]);
    const [loading, setLoading] = useState(true);
    const [searchQuery, setSearchQuery] = useState('');
    const [isRefreshing, setIsRefreshing] = useState(false);

    // Define the cache keys
    const CACHE_KEY = 'cachedExercises';
    const CACHE_TIMESTAMP_KEY = 'cachedExercisesTimestamp';
    const MAX_CACHE_AGE = 24 * 60 * 60 * 1000; // 24 hours in milliseconds

    useEffect(() => {
        loadCachedExercises();
    }, [authToken]); // Added authToken as a dependency to reload when token changes

    // Debounced fetch function to prevent multiple rapid API calls
    const debouncedFetchExercises = useCallback(
        debounce((query) => {
            fetchExercises(query);
        }, 300),
        [authToken]
    );

    useEffect(() => {
        // Trigger debounced search whenever searchQuery changes
        if (searchQuery.length === 0 || searchQuery.length > 2) { // Trigger search after 3 characters
            debouncedFetchExercises(searchQuery);
        }
    }, [searchQuery, debouncedFetchExercises]);

    /**
     * Load exercises from SecureStore
     */
    const loadCachedExercises = async () => {
        if (!authToken) {
            setLoading(false);
            return;
        }

        try {
            const [cachedData, cachedTimestamp] = await Promise.all([
                SecureStore.getItemAsync(CACHE_KEY),
                SecureStore.getItemAsync(CACHE_TIMESTAMP_KEY),
            ]);

            if (cachedData && cachedTimestamp) {
                const parsedData = JSON.parse(cachedData);
                const timestamp = parseInt(cachedTimestamp, 10);
                const currentTime = Date.now();
                const cacheAge = currentTime - timestamp;

                if (cacheAge < MAX_CACHE_AGE) {
                    setExercises(parsedData);
                    console.log('Loaded exercises from SecureStore.');
                    setLoading(false);
                    return;
                } else {
                    console.log('Cache is stale. Fetching fresh data.');
                }
            } else {
                console.log('No cached data found. Fetching from API.');
            }

            // Fetch fresh data
            fetchExercises();
        } catch (error) {
            console.error('Error loading cached exercises:', error);
            Alert.alert('Error', 'Failed to load cached exercises.');
            fetchExercises();
        }
    };

    /**
     * Fetch exercises from the API
     * @param {string} query - Search query
     */
    const fetchExercises = async (query = '') => {
        if (!authToken) {
            Alert.alert('Authentication Error', 'Please log in to fetch exercises.');
            setLoading(false);
            return;
        }

        console.log('Fetching exercises with token:', authToken);

        try {
            const response = await axios.get('http://10.0.0.203:5000/api/exercises', {
                headers: {
                    Authorization: `Bearer ${authToken}`,
                },
                params: {
                    search: query,
                    limit: 50,
                },
            });

            if (response.status === 200) {
                const fetchedExercises = response.data;
                setExercises(fetchedExercises);

                // Cache the fetched data with a timestamp
                await Promise.all([
                    SecureStore.setItemAsync(CACHE_KEY, JSON.stringify(fetchedExercises)),
                    SecureStore.setItemAsync(CACHE_TIMESTAMP_KEY, Date.now().toString()),
                ]);
                console.log('Fetched exercises from API and cached.');
            } else {
                console.error('Unexpected response status:', response.status);
                Alert.alert('Error', 'Unexpected response from the server.');
            }
        } catch (error) {
            console.error('Error fetching exercises:', error.response ? error.response.data : error.message);
            Alert.alert('Error', 'Failed to fetch exercises from the server.');
        } finally {
            setLoading(false);
            setIsRefreshing(false);
        }
    };

    /**
     * Handle user input in the search bar
     * @param {string} text - Search query text
     */
    const handleSearch = (text) => {
        setSearchQuery(text);
    };

    /**
     * Handle pull-to-refresh action
     */
    const onRefresh = () => {
        setIsRefreshing(true);
        fetchExercises(searchQuery);
    };

    /**
     * Clear cached data manually
     */
    const clearCache = async () => {
        try {
            await SecureStore.deleteItemAsync(CACHE_KEY);
            await SecureStore.deleteItemAsync(CACHE_TIMESTAMP_KEY);
            setExercises([]);
            Alert.alert('Success', 'Cache has been cleared.');
        } catch (error) {
            console.error('Error clearing cache:', error);
            Alert.alert('Error', 'Failed to clear cache.');
        }
    };

    /**
     * Render each exercise item
     * @param {object} item - Exercise object
     */
    const renderItem = ({ item }) => (
        <View style={styles.itemContainer}>
            {item.imageUrl ? (
                <Image source={{ uri: item.imageUrl }} style={styles.image} resizeMode="cover" />
            ) : (
                <View style={[styles.image, styles.imagePlaceholder]}>
                    <Text style={styles.imagePlaceholderText}>No Image</Text>
                </View>
            )}
            <Text style={styles.title}>{item.name}</Text>
            {item.description ? <Text style={styles.description}>{item.description}</Text> : null}
            {item.videoUrl ? (
                <TouchableOpacity onPress={() => Linking.openURL(item.videoUrl)}>
                    <Text style={styles.link}>Video Tutorial</Text>
                </TouchableOpacity>
            ) : null}
        </View>
    );

    if (loading && exercises.length === 0) {
        return (
            <View style={styles.loader}>
                <ActivityIndicator size="large" color="#0000ff" />
            </View>
        );
    }

    return (
        <View style={styles.container}>
            <View style={styles.header}>
                <TextInput
                    style={styles.searchBar}
                    placeholder="Search exercises..."
                    value={searchQuery}
                    onChangeText={handleSearch}
                    autoCapitalize="none"
                    autoCorrect={false}
                    clearButtonMode="while-editing"
                />
                <TouchableOpacity style={styles.clearCacheButton} onPress={clearCache}>
                    <Text style={styles.clearCacheButtonText}>Clear Cache</Text>
                </TouchableOpacity>
            </View>
            <FlatList
                data={exercises}
                keyExtractor={(item) => item._id} // Assuming each exercise has a unique _id
                renderItem={renderItem}
                contentContainerStyle={styles.list}
                keyboardShouldPersistTaps="handled"
                refreshControl={<RefreshControl refreshing={isRefreshing} onRefresh={onRefresh} />}
                ListEmptyComponent={
                    !loading && (
                        <View style={styles.emptyContainer}>
                            <Text style={styles.emptyText}>No exercises found.</Text>
                        </View>
                    )
                }
            />
        </View>
    );
};

const styles = StyleSheet.create({
    container: {
        flex: 1,
    },
    header: {
        flexDirection: 'row',
        alignItems: 'center',
        paddingHorizontal: 10,
        paddingTop: 10,
        paddingBottom: 5,
    },
    searchBar: {
        flex: 1,
        height: 50,
        borderColor: '#ccc',
        borderWidth: 1,
        borderRadius: 8,
        marginRight: 10,
        paddingHorizontal: 15,
        fontSize: 16,
    },
    clearCacheButton: {
        height: 50,
        backgroundColor: '#ff4d4d',
        borderRadius: 8,
        justifyContent: 'center',
        alignItems: 'center',
        paddingHorizontal: 15,
    },
    clearCacheButtonText: {
        color: '#fff',
        fontSize: 14,
        fontWeight: 'bold',
    },
    itemContainer: {
        padding: 10,
        borderBottomWidth: 1,
        borderColor: '#ccc',
    },
    image: {
        width: '100%',
        height: 200,
        marginBottom: 10,
        borderRadius: 10,
    },
    imagePlaceholder: {
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#e0e0e0',
    },
    imagePlaceholderText: {
        color: '#555',
    },
    title: {
        fontSize: 18,
        fontWeight: 'bold',
    },
    description: {
        marginTop: 5,
        fontSize: 14,
        color: '#555',
    },
    link: {
        color: 'blue',
        marginTop: 5,
    },
    loader: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
    },
    list: {
        paddingBottom: 20,
    },
    emptyContainer: {
        flex: 1,
        alignItems: 'center',
        marginTop: 50,
    },
    emptyText: {
        fontSize: 16,
        color: '#555',
    },
});

export default ExercisesScreen;

// context/AuthContext.js

import React, { createContext, useState, useEffect } from 'react';
import * as SecureStore from 'expo-secure-store';

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
    const [authToken, setAuthToken] = useState(null);
    const [loading, setLoading] = useState(true);

    const SIGNIN_KEY = 'authToken';

    const signIn = async (token) => {
        setAuthToken(token);
        try {
            await SecureStore.setItemAsync(SIGNIN_KEY, token);
            console.log('Token stored successfully.');
        } catch (error) {
            console.error('Error storing the token:', error);
        }
    };

    const signOut = async () => {
        setAuthToken(null);
        try {
            await SecureStore.deleteItemAsync(SIGNIN_KEY);
            console.log('Token removed successfully.');
        } catch (error) {
            console.error('Error removing the token:', error);
        }
    };

    const loadToken = async () => {
        try {
            const token = await SecureStore.getItemAsync(SIGNIN_KEY);
            if (token) {
                setAuthToken(token);
                console.log('Token loaded from SecureStore:', token);
            } else {
                console.log('No token found.');
            }
        } catch (e) {
            console.log('Failed to load token:', e);
        } finally {
            setLoading(false);
        }
    };

    useEffect(() => {
        loadToken();
    }, []);

    return (
        <AuthContext.Provider value={{ authToken, signIn, signOut }}>
            {!loading && children}
        </AuthContext.Provider>
    );
};

// routes/exercises.js
const express = require('express');
const router = express.Router();
const ExerciseService = require('../services/exerciseService');
const auth = require('../middleware/auth');

// GET /api/exercises
router.get('/', auth, async (req, res) => {
    console.log('GET /api/exercises called');
    const userId = req.user;
    const { search, muscle, equipment, difficulty, gender } = req.query;

    try {
        // Fetch user data
        const User = require('../models/User');
        const user = await User.findById(userId);

        if (!user) {
            console.warn(`User with ID ${userId} not found.`);
            return res.status(404).json({ message: 'User not found.' });
        }

        // For testing, remove baseFilters
        const filters = {
            search: search || '',
            muscle: muscle || '',
            equipment: equipment || '',
            difficulty: difficulty || '',
            gender: gender || '',
        };

        console.log(`Filters applied: ${JSON.stringify(filters)}`);

        const exercises = await ExerciseService.searchExercises(filters);
        console.log(`Fetched ${exercises.length} exercises for user ${userId}`);
        res.json(exercises);
    } catch (err) {
        console.error('Error fetching exercises:', err);
        res.status(500).json({ error: 'Internal server error' });
    }
});

module.exports = router;

// routes/auth.js
const express = require('express');
const router = express.Router();
const jwt = require('jsonwebtoken');
const User = require('../models/User');

// Register Route
router.post('/register', async (req, res) => {
    const { username, email, password, desiredPhysique, fitnessGoals, dietaryPreferences, dietaryRestrictions } = req.body;
    try {
        const existingUser = await User.findOne({ email });
        if (existingUser) {
            return res.status(400).json({ message: 'Email already in use' });
        }

        const newUser = new User({
            username,
            email,
            password,
            desiredPhysique,
            fitnessGoals,
            dietaryPreferences,
            dietaryRestrictions,
        });
        await newUser.save();

        const payload = { userId: newUser._id };
        const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });

        res.status(201).json({ token, message: 'User registered successfully' });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

// Login Route
router.post('/login', async (req, res) => {
    const { email, password } = req.body; // Changed from username to email for consistency
    try {
        const user = await User.findOne({ email });
        if (!user) return res.status(400).json({ message: 'Invalid credentials' });

        const isMatch = await user.comparePassword(password);
        if (!isMatch) return res.status(400).json({ message: 'Invalid credentials' });

        const payload = { userId: user._id };
        const token = jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: '1h' });

        res.json({ token, message: 'Login successful' });
    } catch(err) {
        res.status(500).json({ error: err.message });
    }
});

module.exports = router;

const mongoose = require('mongoose');

const ExerciseSchema = new mongoose.Schema({
    name: { type: String, required: true, index: true },
    musclesTargeted: { type: [String], index: true },
    equipment: { type: [String], index: true },
    difficulty: { type: String, enum: ['beginner', 'intermediate', 'advanced'], default: 'beginner', index: true },
    targetGender: {
        male: { type: Boolean, default: true },
        female: { type: Boolean, default: true },
    },
    description: String,
    imageUrl: String,
    videoUrl: String,
}, { timestamps: true });

// Create a text index for fast searching on `name` and possibly other fields
ExerciseSchema.index({ name: 'text', description: 'text', musclesTargeted: 'text' });

module.exports = mongoose.model('Exercise', ExerciseSchema);

// middleware/auth.js

const jwt = require('jsonwebtoken');

const auth = (req, res, next) => {
    const authHeader = req.header('Authorization');

    if (!authHeader) {
        return res.status(401).json({ message: 'No token, authorization denied' });
    }

    // The expected format is "Bearer <token>"
    const parts = authHeader.split(' ');
    if (parts.length !== 2 || parts[0] !== 'Bearer') {
        return res.status(401).json({ message: 'Token is not valid' });
    }

    const token = parts[1];

    try {
        const decoded = jwt.verify(token, process.env.JWT_SECRET);
        req.user = decoded.userId; // Assuming your token payload contains userId
        next();
    } catch (err) {
        console.error('Token verification failed:', err);
        res.status(401).json({ message: 'Token is not valid' });
    }
}

module.exports = auth;

// controllers/authController.js

const jwt = require('jsonwebtoken');
const User = require('../models/User'); // Adjust the path as necessary

// Function to generate JWT
const generateToken = (userId) => {
    return jwt.sign(
        { userId },
        process.env.JWT_SECRET, // Ensure this matches the secret in auth.js
        { expiresIn: '1h' }      // Token validity duration
    );
};

// Login Function
const login = async (req, res) => {
    const { username, password } = req.body;

    try {
        // Find user by username
        const user = await User.findOne({ username });
        if (!user) {
            return res.status(400).json({ message: 'Invalid credentials' });
        }

        // Check if password matches
        const isMatch = await user.comparePassword(password); // Implement comparePassword in your User model
        if (!isMatch) {
            return res.status(400).json({ message: 'Invalid credentials' });
        }

        // Generate token
        const token = generateToken(user._id);

        // Return token to client
        res.json({ token });
    } catch (err) {
        console.error('Login error:', err);
        res.status(500).json({ message: 'Server error' });
    }
};

// Register Function (Optional)
const register = async (req, res) => {
    const { username, password } = req.body;

    try {
        // Check if user already exists
        let user = await User.findOne({ username });
        if (user) {
            return res.status(400).json({ message: 'User already exists' });
        }

        // Create new user
        user = new User({ username, password });
        await user.save();

        // Generate token
        const token = generateToken(user._id);

        // Return token to client
        res.json({ token });
    } catch (err) {
        console.error('Registration error:', err);
        res.status(500).json({ message: 'Server error' });
    }
};

module.exports = { login, register };