const ObjectId = require('mongodb').ObjectId;
const getHash = require('pbkdf2-password')();
const { getDB } = require('../../../utils/mongo');

const USERS_COLLECTION = 'users';
const RECIPES_COLLECTION = 'recipes_collection';
const FAVORITES_USER = 'favorites_user'
let db =null;

const connect = async () => {
    db = await getDB('edateam');
};

const init = async () => {
    await connect();
};

init();

const _idToId = (data) => {
    const { _id, ...rest } = data;
    return {
        id: _id,
        ...rest
    };
}

const _idToArray = (data) => {
    const _idToMap = data.map((item) => _idToId(item));
    return _idToMap;
}

const getResponse = (error, data, success = true) => {
    if (error) {
        return {
            success: false,
            error,
        }
    }

    return {
        success,
        data,
    }
}

const signUp = async ({ email, login, password }) => {
    try {
    db = await getDB('edateam');
    const userCollection = db.collection(USERS_COLLECTION);

    const userData = await userCollection.findOne({
        $or: [
            { login },
            { email }
        ]
    })

    if (userData?.login === login) {
        throw new Error('This login already in db!\nPlease come up with another login!');
    }

    if (userData?.email === email) {
        throw new Error('This email already in db!\nPlease come up with another email!');
    }
    
    return new Promise((resolve, reject) => {
        getHash({ password }, async (err, pass, salt, hash) => {
            if (err) {
                return reject(err);
            }
            const insertedCount  = await userCollection.insertOne({ email, login, password: hash, salt });
            
            if (!insertedCount) {
                return reject(new Error('Insert error!'));
            }
            resolve({});
        });
    });
   
    } catch (error) {
        console.error(error);
        throw error;
    }
};

const getUser = async ({ email }) => {
    if (db === null) {
        throw new Error('no db connection :((');
    }

    try {
        const userCollection = db.collection(USERS_COLLECTION);
        const userData = await userCollection.findOne({ email });
        if (userData) {
            return userData;
        }
        throw new Error('Wrong email or password!');
    } catch (error) {
        throw new Error(error);
    }
}

const getListRecipes = async () => {
    try {
        db = await getDB('edateam');
        const recipesCollection = db.collection(RECIPES_COLLECTION);
        const recipesData = await recipesCollection.find().toArray();

        if (recipesData.length > 0) {
            return _idToArray(recipesData);
        } else {
            throw new Error('No recipes found in the database!');
        }
    } catch (error) {
        console.error('Error in getListRecipes:', error.message);
        throw new Error(error.message);
    }
};

const getRecipe = async (dishId ) => {
    try {
        db = await getDB('edateam');
        const recipesCollection = db.collection(RECIPES_COLLECTION);
        const id = dishId.id;
        const recipeData = await recipesCollection.findOne({ _id :new ObjectId(id) } );
        if (recipeData!=null) {
            return _idToId(recipeData);
        }

        throw new Error('Not found recipe');
    } catch (error) {
        throw new Error(error);
    }
}

const addRecipe = async (recipe) => {
    try {
        db = await getDB('edateam');
        const recipesCollection = db.collection(RECIPES_COLLECTION);
        const result = await recipesCollection.insertOne(recipe);
        
        if (!result.insertedId) {
            throw new Error('Recipe insertion failed');
        }
        
        return {
            success: true,
            id: result.insertedId
        };
    } catch (error) {
        console.error('Error in addRecipe:', error.message);
        throw new Error(error.message);
    }
};


const requiredFields = (fields) => (req, res, next) => {
    // eslint-disable-next-line no-restricted-syntax
    for (const fieldName of fields) {
        if (!req.body[fieldName]) {
            throw new Error(`Параметр ${fieldName} не установлен`)
        }
    }

    next()
}

const addFavorite = async (userId, recipeId) => {
    
    try {
        db = await getDB('edateam');
        const favoritesCollection = db.collection(FAVORITES_USER);
        const result = await favoritesCollection.updateOne(
            { userId: new ObjectId(userId) },
            { $addToSet: { favorites: new ObjectId(recipeId) } },
            { upsert: true }
        );
        return result;
    } catch (error) {
        throw new Error('Error adding favorite: ' + error.message);
    }
};

const getFavorites = async (userId) => {
    try {
        db = await getDB('edateam');
        const favoritesCollection = db.collection(FAVORITES_USER);
        const userFavorites = await favoritesCollection.findOne({ userId: new ObjectId(userId) });

        if (!userFavorites || !userFavorites.favorites || userFavorites.favorites.length === 0) {
            return [];
        }

        const recipesCollection = db.collection(RECIPES_COLLECTION);
        const favoriteRecipes = await recipesCollection.find({ _id: { $in: userFavorites.favorites } }).toArray();

        return favoriteRecipes;
    } catch (error) {
        console.error(error);
        throw new Error("Failed to get user favorites with recipes");
    }
};

module.exports = {
    getUser,
    signUp,
    getResponse,
    _idToId,
    _idToArray,
    getListRecipes,
    getRecipe,
    addRecipe,
    requiredFields, 
    getFavorites,
    addFavorite
};
