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

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

// This router is mounted at "/products" ... 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("/", loadProducts);     // Load the matching products
router.get("/", respondProducts);  // Send the response

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

// These handle GET and PUT requests for a specific product's details.
// Notice how an array of your functions (or middleware) can be supplied and then each is called
router.get("/:id", getProduct, sendSingleProduct);  // handles GET request to /products/746
router.put("/:id", express.json(), saveProduct);    // handles PUT request to /products/746



// 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 product names
//    minprice: the minimum price to look for in the resulting list
//    maxprice: the maximum price to look for in the resulting list
function queryParser(req, res, next) {
	const MAX_PRODUCTS = 50;

    // Try to convert the limit parameter to a number <= MAX_PRODUCTS
	try {
        // If the limit parameter is missing, use a default of 10
        if (!req.query.limit) 
		    req.query.limit = 10;
		else {
            req.query.limit = Number(req.query.limit);
            if (req.query.limit > MAX_PRODUCTS) 
			    req.query.limit = MAX_PRODUCTS;
        }
	} catch {
		// Set a default value (or undefined/ignore) if parsing fails
		req.query.page = 1;
	}

	// Try to convert the page parameter to a number > 0
	try {
		if (!req.query.page)
			req.query.page = 1;
		else {
			req.query.page = Number(req.query.page);
			if (req.query.page < 1)
				req.query.page = 1;
		}
	} catch {
        // Set a default value (or undefined/ignore) if parsing fails
		req.query.page = 1;
	}

    // Try to convert the minprice and maxprice parameters to numbers
	if (req.query.minprice) {
		try {
			req.query.minprice = Number(req.query.minprice);
		} catch (err) {
			req.query.minprice = undefined;
		}
	}
	if (req.query.maxprice) {
		try {
			req.query.maxprice = Number(req.query.maxprice);
		 }catch {
			req.query.maxprice = undefined;
		}
	}

	// Now, build up a matching query string to allow pagination
	let params = [];
	for (let param in req.query) {
		// We want everything except for the page query parameters.
		// We will add the page manually for next/previous page links
		if (param == "page") {
			continue;
		}
		params.push(param + "=" + req.query[param]);
	}
	req.qstring = params.join("&");

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


// Load a product and add it to the request object
function getProduct(req, res, next) {
    // Get the file for the product whose ID is in the request parameters
    let fileName = path.join(".", req.app.locals.config.productDir, req.params.id + ".json");

    // If the file exists, load the product's details, parse it and 
    // set the product property to the loaded detail data before calling the next middleware
	if (fs.existsSync(fileName)) {
		fs.readFile(fileName, "utf-8", function(err, data) {
			if (err) {
				console.log("Error reading file: " + err);
				res.status(500).send("Error reading data on server.");
				return;
			}
			req.product = JSON.parse(data);
			next();
		})
	} else {
		res.status(404).send("Could not find product with ID: " + req.params.id);
	}
}


// Save changes to a product that are given in the request body (i.e., from a PUT).
function saveProduct(req, res, next) {
    // Get the file for the product whose ID is in the request parameters
    let fileName = path.join(".", req.app.locals.config.productDir, req.params.id + ".json");

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


// Helper function for determining whether a product matches the query parameters. 
// It compares the name, min price and max price ... all must be true.
function productMatch(product, query) {
	let nameCheck = !query.name || product.name.toLowerCase().includes(query.name.toLowerCase());
	let minPriceCheck = !query.minprice || product.price >= query.minprice;
	let maxPriceCheck = !query.maxprice || product.price <= query.maxprice;
	return nameCheck && minPriceCheck && maxPriceCheck;
}


// Load the correct set of products based on the query parameters.
// This code adds a products property to the response object which is used later to send the response.
// The code works similar to the user router, but has different checks for product matching.
function loadProducts(req, res, next) {
	let results = [];
	let startIndex = (req.query.page-1) * Number(req.query.limit);

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

		for (let fileNum=0; fileNum < items.length; fileNum++) {
			// Read the data from the product's file and parse the JSON object
			let data = fs.readFileSync(path.join(".", req.app.locals.config.productDir, items[fileNum]));
			let product = JSON.parse(data);

			// Check if the product matches the query parameters
			if (productMatch(product, req.query)) {
				// Add it to the results if at least at the correct index for the page
				if (count >= startIndex) 
					results.push(product);
				// If we have reached the limit for results, then stop reading
				if (results.length >= req.query.limit)
					break;
				count++;  // Increase the count ... we need this for pagination
			}
		}

        // Set the property on the result object and call the next middleware function
		res.products = results;
        next();
	});
}


// Send a response (as either the products page or a JSON object of products)
function respondProducts(req, res, next) {
	res.format({
		"text/html": () => {
            res.render("pages/products", {products: res.products, qstring: req.qstring, current: req.query.page });
        },
		"application/json": () => {
            res.status(200).json(res.products);
        }
	});
}


// Create a new product with fake details. In a real system, we could extract the product information
// from the request (e.g., either from some form data or JSON from the client-side).
function createProduct(req, res, next) {
	// Look at the request body, which normally would contain the product specifications.
	// We can check the data, validate it and create the new product as needed.
	console.log(req.body);

	let p = {};  // Create the product
	p.id = req.app.locals.config.nextProductID;
	p.name = faker.commerce.productName();
	p.price = faker.commerce.price();
	p.reviews = [];
	p.buyers = [];

	// Update the config file and save the product info to a new file
	req.app.locals.config.nextProductID++;
	fs.writeFileSync("config.json", JSON.stringify(req.app.locals.config));
	fs.writeFileSync(path.join(".", req.app.locals.config.productDir, p.id + ".json"), JSON.stringify(p));
	res.status(201).send(p);
}


// Send a single product 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 sendSingleProduct(req, res, next) {
	res.format({
		"application/json": function() {
			res.status(200).json(req.product);
		},
		"text/html": () => { res.render("pages/product", {product: req.product}); }
	});
}

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