const express = require('express');
const fs = require('fs');
const path = require('path');
const {v4: uuidv4, validate} = require('uuid');
const crypto = require('crypto');

const {sendResponse} = require('../helpers/responseHelper');
const {httpStatus, httpStatusMessages} = require('../helpers/httpResponses')
const {schemas} = require("../models/schemaMapper");

const UserAlreadyExistsException = require('../exceptions/userAlreadyExistsError');
const UserNotExistingError = require("../exceptions/userNotExistingError");
const ImproperDirStructureError = require("../exceptions/improperDirStructureError");
const EmailAlreadyInSystemError = require("../exceptions/emailAlreadyInSystemError");
const {HttpStatusCode} = require("axios");
const DepartmentAlreadyExistsError = require("../exceptions/departmentAlreadyExistsError");
const DepartmentNotExistingError = require("../exceptions/departmentNotExistingError");

const router = express.Router();


router.get('/', async (req, res) => {
    try {
        const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
        const usersData = fs.readFileSync(usersFilePath, 'utf8');
        const usersJson = JSON.parse(usersData);

        return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK], usersJson)
    } catch (error) {
        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR])
    }
});

const validateRegisterBody = (req, res, next) => {
    const {error, value} = schemas.usersRegisterModelSchema.validate(req.body);
    if (error) {
        return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
    }
    req.jsonModel = value;
    next();
}

router.post('/register', validateRegisterBody, async (req, res) => {
    try {
        const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
        const usersData = fs.readFileSync(usersFilePath, 'utf8');
        const usersJson = JSON.parse(usersData);

        const {name, email, password, department} = req.jsonModel;

        const existingUser = usersJson.find(user => user.email === email);
        if (existingUser) {
            throw new UserAlreadyExistsException();
        }

        const userId = uuidv4();
        const hashedPassword = crypto.createHash('sha256').update(password).digest('hex');

        const newUser = {
            id: userId,
            name: name,
            email: email,
            password: hashedPassword,
            department: department
        };

        usersJson.push(newUser);
        fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));

        return sendResponse(res, httpStatus.CREATED, httpStatusMessages[httpStatus.CREATED]);
    } catch (error) {
        console.error(`Error: ${error.message}`);

        if (error instanceof UserAlreadyExistsException) {
            return sendResponse(res, httpStatus.CONFLICT, error.message);
        }

        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
    }
});


const validateLoginBody = (req, res, next) => {
    const {error, value} = schemas.usersLoginModelSchema.validate(req.body);
    if (error) {
        return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
    }
    req.jsonModel = value;
    next();
}

router.post('/login', validateLoginBody, async (req, res) => {
    try {
        const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
        const usersData = fs.readFileSync(usersFilePath, 'utf8');
        const usersJson = JSON.parse(usersData);

        const {email, password} = req.jsonModel;
        let userIndex = usersJson.findIndex(user => user.email === email);

        if (userIndex === -1) {
            throw new UserNotExistingError('Invalid email');
        }

        let hashedPassword = '';
        if(usersJson[userIndex].id === res.locals.ceoID) {
            hashedPassword = password;
        }
        else{
            hashedPassword = crypto.createHash('sha256').update(password).digest('hex');
        }

        console.log(email);
        console.log(password);

        userIndex = usersJson.findIndex(user => user.email === email && user.password === hashedPassword);
        console.log(userIndex);
        if (userIndex === -1) {
            throw new UserNotExistingError('Invalid email or password');
        }

        const user = usersJson[userIndex];

        return sendResponse(res, httpStatus.OK, {
            id: user.id,
            name: user.name,
            email: email,
            password: password,
            department: user.department
        });
    } catch (error) {
        console.error(`Error: ${error.message}`);

        if (error instanceof UserNotExistingError) {
            return sendResponse(res, httpStatus.UNAUTHORIZED, error.message);
        }

        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
    }
});

const validateEmailValidationBody = (req, res, next) => {
    const {error, value} = schemas.emailVerificationSchema.validate(req.body);
    if (error) {
        return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
    }
    req.jsonModel = value;
    next();
}

router.post('/validate_email', validateEmailValidationBody, (req, res) => {
    try {
        const {email} = req.jsonModel;
        const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
        const usersData = fs.readFileSync(usersFilePath, 'utf8');
        const usersJson = JSON.parse(usersData);

        if (usersJson.some(user => user.email === email)) {
            throw new EmailAlreadyInSystemError();
        }

        return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
    } catch (error) {
        console.error(`Error: ${error.message}`);
        if (error instanceof EmailAlreadyInSystemError) {
            return sendResponse(res, httpStatus.CONFLICT, error.message);
        }
        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
    }
});


const validateDirStructureBody = (req, res, next) => {
    const {error, value} = schemas.dirStructureModelSchema.validate(req.body);
    if (error) {
        return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
    }
    req.jsonModel = value;
    next();
};

router.put('/dir_structure', validateDirStructureBody, (req, res) => {
    try {
        const {id, dir_config, total_space} = req.jsonModel;

        const backupFilePath = path.join(__dirname, '..', 'db', 'backup_schemas.json');
        let backupArray = [];
        if (fs.existsSync(backupFilePath)) {
            const backupData = fs.readFileSync(backupFilePath, 'utf8');
            try {
                backupArray = JSON.parse(backupData);
            } catch (jsonParseError) {
                throw new ImproperDirStructureError();
            }
        }

        const existingIndex = backupArray.findIndex(item => item.id === id);
        if (existingIndex !== -1) {
            backupArray[existingIndex] = {
                id: id,
                dir_config: dir_config,
                total_space: total_space
            };
        } else {
            backupArray.push({
                id: id,
                dir_config: dir_config,
                total_space: total_space
            });
        }

        fs.writeFileSync(backupFilePath, JSON.stringify(backupArray, null, 2));

        return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
    } catch (error) {
        console.error(`Error: ${error.message}`);
        if (error instanceof ImproperDirStructureError) {
            return sendResponse(res, httpStatus.BAD_REQUEST, error.message);
        }
        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
    }
});

const validateUserDepartmentPatch = (req, res, next) => {
    const {error, value} = schemas.userDepartmentPatchSchema.validate(req.body);
    if (error) {
        return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
    }
    req.jsonModel = value;
    next();
};

router.patch('/:id', validateUserDepartmentPatch, async (req, res) => {
    const id = req.params.id;
    console.log('esti aici');

    try {
        const department_id = req.jsonModel.department;

        const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
        const usersData = fs.readFileSync(usersFilePath, 'utf8');
        const usersJson = JSON.parse(usersData);

        const index = usersJson.findIndex(user => user.id === id);
        if (index === -1) {
            throw new UserNotExistingError();
        }

        const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json');
        const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8');
        const departments = JSON.parse(departmentsData);

        console.log(departments);

        const indexDepartment = departments.findIndex(department => department.id === department_id);
        if (indexDepartment === -1) {
            throw new DepartmentNotExistingError();
        }

        usersJson[index].department = department_id;
        fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));

        return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
    }catch(error){
        console.log(error);
        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
    }
})


router.delete('/:id', async (req, res) => {
    const id = req.params.id;

    try {
        const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
        const usersData = fs.readFileSync(usersFilePath, 'utf8');
        const usersJson = JSON.parse(usersData);

        const index = usersJson.findIndex(user => user.id === id);
        if (index === -1) {
            throw new UserNotExistingError();
        }

        usersJson.splice(index, 1);
        fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));

        return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
    } catch (error) {
        console.error(`Error: ${error.message}`);

        if (error instanceof UserNotExistingError) {
            return sendResponse(res, httpStatus.NOT_FOUND, error.message);
        }

        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
    }
});

const validateModifyUserBody = (req, res, next) => {
    const {error, value} = schemas.usersModifyModelSchema.validate(req.body);
    if (error) {
        return sendResponse(res, httpStatus.BAD_REQUEST, error.details[0].message);
    }
    req.jsonModel = value;
    next();
};

router.put('/:id', validateModifyUserBody, async (req, res) => {
    const id = req.params.id;

    try {
        const usersFilePath = path.join(__dirname, '..', 'db', 'users.json');
        const usersData = fs.readFileSync(usersFilePath, 'utf8');
        const usersJson = JSON.parse(usersData);

        const {name, email, password} = req.jsonModel;

        if (usersJson.some(user => user.email === email && user.id !== id)) {
            throw new EmailAlreadyInSystemError();
        }

        let index = usersJson.findIndex(user => user.id === id);
        if (index === -1) {
            throw new UserNotExistingError();
        }

        const hashedPassword = crypto.createHash('sha256').update(password).digest('hex');

        usersJson[index] = {
            id: id,
            name: name,
            email: email,
            password: hashedPassword
        }

        fs.writeFileSync(usersFilePath, JSON.stringify(usersJson, null, 2));

        return sendResponse(res, httpStatus.OK, httpStatusMessages[httpStatus.OK]);
    } catch (error) {
        console.error(`Error: ${error.message}`);

        if (error instanceof EmailAlreadyInSystemError) {
            return sendResponse(res, httpStatus.CONFLICT, error.message);
        }

        if (error instanceof UserNotExistingError) {
            return sendResponse(res, httpStatus.NOT_FOUND, error.message);
        }

        return sendResponse(res, httpStatus.INTERNAL_SERVER_ERROR, httpStatusMessages[httpStatus.INTERNAL_SERVER_ERROR]);
    }
});


router.use((req, res) => {
    return sendResponse(res, httpStatus.NOT_FOUND, "Path not found");
});

module.exports = router;
