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

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

const DepartmentAlreadyExistsError = require("../exceptions/departmentAlreadyExistsError");
const InvalidDepartmentJsonError = require("../exceptions/invalidDepartmentJsonError");
const DepartmentNotExistingError = require("../exceptions/departmentNotExistingError");

const router = express.Router();

router.get('/', (req, res) => {
    try {
        const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json');
        const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8');
        const departments = JSON.parse(departmentsData);


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


function generateAESKey() {
    return crypto.randomBytes(32).toString('hex');
}

router.post('/', validateRegisterBody, createDepartment, addSecurityLevel, (req, res) => {
    return res;
});

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

function createDepartment(req, res, next) {
    try {
        const { name } = req.jsonModel;

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

        const existingDepartment = departments.find(department => department.name === name);
        if (existingDepartment) {
            throw new DepartmentAlreadyExistsError();
        }

        const departmentId = uuidv4();
        const aesKey = generateAESKey();
        const newDepartment = {
            id: departmentId,
            name: name,
            key: aesKey
        };

        departments.push(newDepartment);
        fs.writeFileSync(departmentsFilePath, JSON.stringify(departments, null, 2));

        req.departmentId = departmentId;
        next();

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

function addSecurityLevel(req, res) {
    try {
        const { departmentId } = req;

        const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
        const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8');
        const securityLevels = JSON.parse(securityLevelsData);

        const nextIndex = Object.keys(securityLevels).length + 1;
        securityLevels[nextIndex] = departmentId;

        fs.writeFileSync(securityLevelsFilePath, JSON.stringify(securityLevels, null, 2));

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


router.put('/', validateDepartmentsJson, validateSecurityLevels, updateSecurityLevels);

function validateDepartmentsJson(req, res, next) {
    try {
        for (const key in req.body) {
            if (!Number.isInteger(parseInt(key)) || typeof req.body[key] !== 'string') {
                throw new InvalidDepartmentJsonError();
            }
        }

        next();
    } catch (error) {
        console.error(`Error: ${error.message}`);
        return sendResponse(res, httpStatus.BAD_REQUEST, error.message);
    }
}

function validateSecurityLevels(req, res, next) {
    try {
        const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
        const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8');
        const securityLevels = JSON.parse(securityLevelsData);

        if (Object.keys(req.body).length !== Object.keys(securityLevels).length) {
            throw new InvalidDepartmentJsonError('Number of keys does not match the database');
        }

        const encounteredValues = new Set();
        const securityLevelValues = Object.values(securityLevels);

        for (const key in req.body) {
            const requestBodyValue = req.body[key];
            if (!securityLevels.hasOwnProperty(key)) {
                throw new InvalidDepartmentJsonError('Invalid key found in request body');
            }
            const securityLevelValue = securityLevels[key];

            if (!securityLevelValues.includes(securityLevelValue)) {
                throw new InvalidDepartmentJsonError('Values do not match the database');
            }

            if (encounteredValues.has(requestBodyValue)) {
                throw new InvalidDepartmentJsonError('Duplicate values found in request body');
            }

            encounteredValues.add(requestBodyValue);
        }

        next();
        return res;
    } catch (error) {
        console.error(`Error: ${error.message}`);
        return sendResponse(res, httpStatus.BAD_REQUEST, error.message);
    }
}

function updateSecurityLevels(req, res) {
    try {
        const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
        fs.writeFileSync(securityLevelsFilePath, JSON.stringify(req.body, null, 2));

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


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

    try {
        deleteDepartment(id, res);
    } catch (error) {
        console.error(`Error: ${error.message}`);

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

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

function deleteDepartment(id, res) {
    const departmentsFilePath = path.join(__dirname, '..', 'db', 'departments.json');
    const departmentsData = fs.readFileSync(departmentsFilePath, 'utf8');
    const departmentsJson = JSON.parse(departmentsData);

    const index = departmentsJson.findIndex(department => department.id === id);
    if (index === -1) {
        throw new DepartmentNotExistingError();
    }

    departmentsJson.splice(index, 1);
    fs.writeFileSync(departmentsFilePath, JSON.stringify(departmentsJson, null, 2));

    const securityLevelsFilePath = path.join(__dirname, '..', 'db', 'security_levels.json');
    const securityLevelsData = fs.readFileSync(securityLevelsFilePath, 'utf8');
    const securityLevelsJson = JSON.parse(securityLevelsData);

    const securityIndex = securityLevelsJson.findIndex(level => level.id === id);
    if (securityIndex !== -1) {
        securityLevelsJson.splice(securityIndex, 1);
        fs.writeFileSync(securityLevelsFilePath, JSON.stringify(securityLevelsJson, null, 2));
    }

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



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

module.exports = router;
