import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import path from 'path';
import { Jimp } from 'jimp';
import fs from 'fs';

import UserService from "../services/user.js";
import utils from '../utils.js';
import config from '../config.js';

class UserController {
    async register(req, res) {
        const { username, password } = req.body;

        if (username.length > 16) {
            return res.status(400).send("Nickname is too long! Maximum length is 16 characters.");
        }

        const usernameRegexp = new RegExp("^[a-zA-Z0-9_]{2,16}$");

        if (!usernameRegexp.test(username)) {
            return res.status(400).send("Nickname can only contain alphanumeric and underscores.");
        }

        const hashedPassword = await bcrypt.hash(password, 8);

        await UserService.register(username, hashedPassword);

        if (config.require_token && config.delete_token_on_use) {
            utils.removeFromFile('./inviteTokens.txt', req.body.inviteToken);
        }

        const token = jwt.sign({ username }, config.secret, {expiresIn: "1y"});
        res.cookie("jwt", token);
        return res.status(200).send("Ok");
    }

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

        const storedPassword = await UserService.getPassword(username);

        if (!bcrypt.compareSync(password, storedPassword)) {
            return res.status(403).send("Password is not correct");
        }
        const token = jwt.sign({ username }, config.secret, {expiresIn: "1y"});
        res.cookie("jwt", token);
        return res.status(200).send("Ok");
    }

    async changePassword(req, res) {
        const token = req.cookies["jwt"];
        const username = jwt.decode(token).username;
        const { oldPassword, newPassword } = req.body;

        const storedPassword = await UserService.getPassword(username);

        if (!bcrypt.compareSync(oldPassword, storedPassword)) {
            return res.status(403).send("Password is not correct");
        }

        const newHashedPassword = await bcrypt.hash(newPassword, 8);

        await UserService.changePassword(username, newHashedPassword);

        return res.status(200).send("Successful");
    }

    async logout(req, res) {
        req.session.destroy();
        res.clearCookie("jwt");
        return res.redirect("/login");
    }

    async uploadSkin(req, res) {
        const token = req.cookies["jwt"];
        const decoded = jwt.decode(token);
        const tempPath = req.file.path;
        const targetPath = config.skins_target_directory + `/${decoded.username}.png`;

        if (path.extname(req.file.originalname).toLowerCase() !== ".png") {
            return res.status(400).send("Only .png files are allowed!");
        }

        const image = await Jimp.read(tempPath);
	const allowedWidths = [32, 64, 128];
	const allowedHeights = [32, 64];
        if (!allowedWidths.includes(image.bitmap.width) || !allowedHeights.includes(image.bitmap.height)) {
            return res.status(400).send('This does not look like a minecraft skin.');
        }
		try {
	        await fs.promises.cp(tempPath, targetPath);
	        await fs.promises.unlink(tempPath);
	    } catch (err) {
	    	return res.status(500).send("Ooops! Something went wrong! Please, report to the developer.");
	    }
        return res.status(200).send("Skin uploaded!");
    }

    async uploadCape(req, res) {
        const token = req.cookies["jwt"];
        const decoded = jwt.decode(token);
        const tempPath = req.file.path;
        const targetPath = config.capes_target_directory + `/${decoded.username}.png`;

        if (path.extname(req.file.originalname).toLowerCase() !== ".png") {
            return res.status(400).send("Only .png files are allowed!");
        }
        
        const image = await Jimp.read(tempPath);
        if ((image.bitmap.width != 64 || image.bitmap.height != 32) && (image.bitmap.width != 128 || image.bitmap.height != 64)) {
            return res.status(400).send('This does not look like a minecraft cape.');
        }

        try {
   	        await fs.promises.cp(tempPath, targetPath);
   	        await fs.promises.unlink(tempPath);
   	    } catch (err) {
  	    	return res.status(500).send("Ooops! Something went wrong! Please, report to the developer.");
   	    }

        return res.status(200).send("Cape uploaded!");
    }

    async getUsername(req, res) {
        const token = req.cookies["jwt"];
        return res.status(200).send(jwt.decode(token).username);
    }
}

export default new UserController();
