const { v4: uuid } = require('uuid')
const ObjectId = require('mongodb').ObjectID

const { getDB } = require('../../utils/mongo')
const { _idToId, checkDB, _idToIdArray, filterId } = require('./utils')

let db = null

const USERS_COLL = 'ep_users'
const PROJECTS_COLL = 'ep_all_projects'
const PROJECT_TASKS_COLL = 'ep_projects_tasks'
const PROJECT_MEMBERS_COLL = 'ep_projects_members'
const PROJECT_STATUSES_COLL = 'ep_projects_statuses'
const PROJECT_TYPES_COLL = 'ep_projects_types'

const connect = async () => {
    db = await getDB('easy-project')
    if (db === null) throw new Error('Нет подключения к базе данных')
}

connect()

const hashPassword = (password, salt = uuid(), salt2 = uuid(), salt3 = uuid()) => ({
    password: (password.split('').join(salt) + salt2 + password.split('').join(salt3)).split('-').reverse().join('-'),
    salt,
    salt2,
    salt3,
})

const registrationUser = async ({ email, login, password, ...rest }) => {
    checkDB(db)
    const usersCollection = db.collection(USERS_COLL)
    const userExist = await usersCollection.find({
        $or: [{
            login,
        }, {
            email,
        }],
    }).toArray()

    if (userExist.length) {
        if (userExist[0].login === login) {
            throw new Error('Логин уже занят')
        }
        if (userExist[0].email === email) {
            throw new Error('Email уже занят')
        }
    }

    const { password: hash, salt, salt2, salt3 } = hashPassword(password)

    const user = {
        salt,
        salt2,
        salt3,
        hash,
        login,
        email,
        ...rest,
    }

    const { insertedId } = await usersCollection.insertOne(user)
    user.id = insertedId
}

const signInUser = async ({ email, password }) => {
    checkDB(db)

    const usersCollection = db.collection(USERS_COLL)

    const [userCandidate] = await usersCollection.find({
        email,
    }).toArray()

    if (!userCandidate) {
        throw new Error('Email или пароль не корректный')
    }
    const { salt, salt2, salt3, hash, ...cleanUser } = userCandidate
    const { password: hashFromDb } = hashPassword(password, salt, salt2, salt3)

    if (hash !== hashFromDb) {
        throw new Error('Email или пароль не корректный')
    }

    return cleanUser
}

const getMyProjects = async (userId) => {
    checkDB(db)

    const userFilterById = filterId(userId)

    const projectCollection = db.collection(PROJECTS_COLL)
    const usersCollection = db.collection(USERS_COLL)

    let projectList = await projectCollection.find({
        $or: [{
            userId,
        }, {
            members: {
                $in: [userId],
            },
        }],
    }).toArray()

    if (projectList) {
        const [userAuthor] = await usersCollection.find(userFilterById).toArray()
        for (let index = 0; index < projectList.length; index++) {
            projectList[index].author = userAuthor
            projectList[index] = _idToId(projectList[index])
        }
        return projectList
    }
    return []
}

const deleteProjectById = async (projectId) => {
    checkDB(db)

    const projectCollection = db.collection(PROJECTS_COLL)
    const projectFilterById = filterId(projectId)
    const deleted = await projectCollection.deleteOne(projectFilterById)
    return deleted
}

const getMyProjectById = async (userId, projectId) => {
    checkDB(db)

    const projectCollection = db.collection(PROJECTS_COLL)
    const usersCollection = db.collection(USERS_COLL)

    const userFilterById = filterId(userId)

    const [userAuthor] = await usersCollection.find(userFilterById).toArray()

    const projectFilter = filterId(projectId)

    let [projectMyExist] = await projectCollection.find(projectFilter).toArray()

    const members = await usersCollection.find({
        _id: {
            $in: projectMyExist.members.map((memberId) => new ObjectId(memberId)),
        },
    }).toArray()

    projectMyExist.members = members.map((m) => ({
        value: m._id, label: m.email,
    }))
    projectMyExist.author = userAuthor

    projectMyExist = _idToId(projectMyExist)

    return projectMyExist
}

const newProject = async ({
    title,
    code,
    userId,
    members,
}) => {
    checkDB(db)

    if (!title || !code) {
        throw new Error('Fields can\'t be empty')
    }

    const projectCollection = db.collection(PROJECTS_COLL)

    const project = {
        title,
        code,
        userId,
        created: Date.now(),
        changed: Date.now(),
        changedBy: userId,
        taskIndex: 0,
        members,
    }

    await projectCollection.insertOne(project)

    return _idToId(project)
}

const updateProject = async ({ projectId, title, code, members }) => {
    checkDB(db)

    if (!title || !code) {
        throw new Error('Fields can\'t be empty')
    }
    const projectCollection = db.collection(PROJECTS_COLL)

    const projectFilterById = filterId(projectId)

    const updatedProject = await projectCollection.updateOne(projectFilterById, {
        $set: {
            title,
            code,
            changed: Date.now(),
            members,
        },
    })

    return updatedProject
}

// TODO: Совмещение projectId с userId ИЛИ поиск по memberIds
const getProjectById = async ({ projectId }) => {
    checkDB(db)

    const projectFilterById = filterId(projectId)

    const projectCollection = db.collection(PROJECTS_COLL)
    const [projectExist] = await projectCollection.find(projectFilterById).toArray()

    return projectExist
}

const getTaskById = async ({ taskId, userId }) => {
    checkDB(db)
    if (taskId) {
        const userFilterById = filterId(userId)
        const taskFilterById = filterId(taskId)

        const taskCollection = db.collection(PROJECT_TASKS_COLL)
        const usersCollection = db.collection(USERS_COLL)
        let [taskExist] = await taskCollection.find(taskFilterById).toArray()

        if (taskExist) {
            const [userAuthor] = await usersCollection.find(userFilterById).toArray()
            taskExist.author = userAuthor
            return _idToId(taskExist)
        }
    }
    return {
    }
}

const createTask = async ({ taskData, authorId, projectId }) => {
    checkDB(db)

    const projectCollection = db.collection(PROJECTS_COLL)
    const projectFilterById = filterId(projectId)

    const [projectExist] = await projectCollection.find(projectFilterById).toArray()

    if (!projectExist) {
        throw new Error('The project not exists [createTask]')
    }

    const nextIndex = projectExist.taskIndex + 1

    const taskCollection = db.collection(PROJECT_TASKS_COLL)

    const { type, status, ...taskRest } = taskData

    const task = {
        ...taskRest,
        type: Number(type),
        status: Number(status),
        changed: Date.now(),
        created: Date.now(),
        taskIndex: nextIndex,
        authorId,
        projectId,
    }

    await projectCollection.updateOne(projectFilterById, {
        $set: {
            taskIndex: nextIndex,
            projectChanged: Date.now(),
        },
    })

    await taskCollection.insertOne(task)

    return _idToId(task)
}

const getTaskListByProjectId = async ({ projectId }) => {
    checkDB(db)

    const projectFilterById = filterId(projectId)

    const projectCollection = db.collection(PROJECTS_COLL)
    const [projectExist] = await projectCollection.find(projectId).toArray()

    if (!projectExist) {
        throw new Error('The project not exists [getTaskListByProjectId]')
    }

    const taskCollection = db.collection(PROJECT_TASKS_COLL)
    let taskListCandidate = await taskCollection.find({
        projectId,
    }).toArray()

    // if (taskListCandidate.length > 0) {
    //     for (let index = 0; index < taskListCandidate.length; index++) {
    //         // const [userAuthor] = await usersCollection.find(userFilterById).toArray()
    //         // taskListCandidate[index].author = userAuthor
    //         projectExist[index] = _idToId(projectExist[index])
    //     }
    // }

    taskListCandidate = _idToIdArray(taskListCandidate)

    return taskListCandidate
}

const editTask = async ({ taskData, projectId, authorId, taskId }) => {
    checkDB(db)

    const taskCollection = db.collection(PROJECT_TASKS_COLL)
    const projectCollection = db.collection(PROJECTS_COLL)

    const taskFilterById = filterId(taskId)
    const projectFilterById = filterId(projectId)

    const { type, status, ...taskRest } = taskData

    const updatedTask = await taskCollection.updateOne(taskFilterById, {
        $set: {
            type: Number(type),
            status: Number(status),
            ...taskRest,
        },
    })

    await projectCollection.updateOne(projectFilterById, {
        $set: {
            projectChanged: Date.now(),
        },
    })

    return updatedTask
}

const deleteTaskById = async (taskId) => {
    checkDB(db)

    const taskCollection = db.collection(PROJECT_TASKS_COLL)
    const taskFilterById = filterId(taskId)
    const deleted = await taskCollection.deleteOne(taskFilterById)
    return deleted
}

const getAllUsers = async () => {
    checkDB(db)

    const usersCollection = db.collection(USERS_COLL)

    let allUsers = await usersCollection.find().toArray()

    return _idToIdArray(allUsers)
}

// const getProjectMember = async () => {
//     checkDB(db)

//     const usersCollection = db.collection(USERS_COLL)

//     let allUsers = await usersCollection.find().toArray()

//     return _idToIdArray(allUsers)
// }

const updateProjectMembers = async (projectData, members) => {
    checkDB(db)

    const memberCollection = db.collection(PROJECT_MEMBERS_COLL)

    await memberCollection.deleteMany({
        projectId: projectData.id,
    })

    const membersAdd = []

    for (let memberIndex = 0; memberIndex < members.length; memberIndex++) {
        const member = {
            projectId: projectData.id,
            memberId: members[memberIndex].value,
        }
        membersAdd.push(member)
    }
    const { insertedData } = await memberCollection.insertMany(membersAdd)

    return insertedData
}

module.exports = {
    connect,
    registrationUser,
    signInUser,
    getMyProjects,
    deleteProjectById,
    newProject,
    createTask,
    editTask,
    deleteTaskById,
    getTaskListByProjectId,
    getProjectById,
    getMyProjectById,
    getTaskById,
    updateProject,

    getAllUsers,

    updateProjectMembers,
}