// This router code is responsible for handling the queries related to users

const express = require('express');
const path = require('path');
const fs = require("fs");

// First, install this module with "npm install faker" from command line after "npm init"
const faker = require('faker'); // used for generating random data

// This router is mounted at "/users" ... you can see in store-server.js
let router = express.Router();

// These routes are relative to where the router is mounted. They are executed in 
// order since the first two call next() before they return
// These three handle GET requests to /users, one after the other.
router.get("/", queryParser);   // Parse the query parameters
router.get("/", loadUsers);     // Load the matching users
router.get("/", respondUsers);  // Send the response

// Handles POST requests to /users by making use of the JSON body parser
router.post("/", express.json(), createUser); // Parse the body, then create a random user

// These handle GET and PUT requests for a specific user's profile.
// Notice how an array of your functions (or middleware) can be supplied and then each is called
router.get("/:uid", [getUser, sendSingleUser]);   // handles GET request to /users/123
router.put("/:uid", [express.json(), saveUser]);  // handles PUT request to /users/123




// Load a user and add a user property to the request object based on user ID request parameter
function getUser(req, res, next) {
	// Get the file for the user whose ID is in the request parameters
    let fileName = path.join(".", req.app.locals.config.userDir, req.params.uid + ".json");
	
	// If the file exists, load the user's profile, parse it and 
    // set the user property to the loaded profile data before calling the next middleware
	if (fs.existsSync(fileName)) {
		let data = fs.readFileSync(fileName);
		req.user = JSON.parse(data);
		next();
	} else {
		res.status(404).send("Could not find user with ID: " + req.params.uid);
	}
}


// Save changes to a user that are given in the request body (i.e., from a PUT).
// More advanced parsing could be done here. For example, updating only the fields 
// included in the body.  We should also perform some data validation here too,
// possibly in a previous middleware.
function saveUser(req, res, next) {
    // Get the file for the user whose ID is in the request parameters
	let fileName = path.join(".", req.app.locals.config.userDir, req.params.uid + ".json");

	// Only update the user if there is already a user profile with the specified ID
	if (fs.existsSync(fileName)) {
		fs.writeFileSync(fileName, JSON.stringify(req.body));
		res.status(200).send("User saved.");
	} else {
		res.status(404).send("Could not find user with ID: " + req.params.uid);
	}
}


// Parse the query parameters:
//    limit: integer specifying maximum number of results to send back
//    page:  integer specifying page of results to send back (start is (page-1)*limit)
//    name:  string to match in the list of user names
function queryParser(req, res, next) {
	const MAX_USERS = 50;

	// Query string values are strings by default, so we should try to do type conversion here for safety.
	// Whether you send an error for invalid data or use some default value is a design decision.

	// If the limit parameter is missing, use a default of 10
	if (!req.query.limit) 
		req.query.limit = 10;

	// If the limit is larger than we allow, use the max
	if (req.query.limit > MAX_USERS) 
		req.query.limit = MAX_USERS;

	// If the page parameter is missing, use a default of 10
	if (!req.query.page)
		req.query.page = 1;

    // If the page is less than 1, use the min
	if (req.query.page < 1)
		req.query.page = 1;
	
	// Try to convert the page parameter to a number
	try {
		req.query.page = Number(req.query.page);
	} catch {
		// Set a default value (or undefined/ignore) if parsing fails
		req.query.page = 1;
	}

	// If there is no name parameter, use * as a wildcard to match everything
	// Although, we could also just check (!req.query.name) later when we need it
	if (!req.query.name)
		req.query.name = "*";

	next(); // Make sure to do this to handle the next middelware
}


// Load the correct set of users based on the query parameters.
// This code adds a users property to the response object which is used later to send the response.
function loadUsers(req, res, next) {
	let results = [];
	let startIndex = (req.query.page-1) * Number(req.query.limit);

	// Read the user files in the directory
	fs.readdir(path.join(".", req.app.locals.config.userDir), function(err, items) {
		let count = 0;

        // If there is a provided query name, then get the data
        for (let fileNum=0; fileNum<items.length; fileNum++) {
			// Read the data from the user's file and parse the JSON object
			let data = fs.readFileSync(path.join(".", req.app.locals.config.userDir, items[fileNum]));
			let user = JSON.parse(data);

			// Check if the user name matches the query
			if (req.query.name == "*" || user.name.toLowerCase().includes(req.query.name.toLowerCase())) {
				// Add them to the results if at least at the correct index for the page
				if (count >= startIndex) {
					 results.push(user);
				}
				// If we have reached the limit for results, then stop reading
				if (results.length >= req.query.limit)
					 break;

				// Increase the count ... we need this for pagination
				count++;
			}
		}
        // Set the property on the result object and call the next middleware function
	    res.users = results;
	    next();
    });
}


// Send a response (as either the users page or a JSON object of users)
function respondUsers(req, res, next) {
    res.format({
        "text/html": () => {
             res.render('pages/users', {
                 users: res.users,
                page: req.query.page,
                name: req.query.name,
                config: req.app.locals.config
            });
        },
        "application/json": () => {
            res.json(res.users);
        }
    });
}


// Create a new user with fake details. In a real system, we could extract the user information
// from the request (e.g., either from some form data or JSON from the client-side).
function createUser(req, res, next) {
	let u = {};  // Create the user

	u.id = req.app.locals.config.nextUserID;
	u.name = faker.name.firstName() + " " + faker.name.lastName();
	u.address = {
        address: faker.address.streetAddress(), 
        city: faker.address.city(), 
        state: faker.address.state(), 
        zip: faker.address.zipCode()};
	u.reviews = [];  // no reviews
	u.products = []; // no purchased products

	// Update the config file and save the user info to a new file
	req.app.locals.config.nextUserID++;
	fs.writeFileSync("config.json", JSON.stringify(req.app.locals.config));
	fs.writeFileSync(path.join(".", req.app.locals.config.userDir, u.id + ".json"), JSON.stringify(u));

	// Respond with the newly created user
	res.status(201).send(u);
}


// Send a single user that is specified as a property in the request object.
// It will send either JSON or an HTML page, depending on the Accepts header
function sendSingleUser(req, res, next) {
    res.format({
        "text/html": () => {
            res.render('pages/user-profile', {
                user: req.user,
                config: req.app.locals.config
            });
        },
        "application/json": () => {
            res.json(req.user);
        }
    });
	next();
}

// Export the router object, so it can be mounted in the store-server.js file
module.exports = router;
