import { all, call, delay, fork, join, put, select, takeEvery, takeLatest, takeLeading } from "redux-saga/effects";
import { actionAboutMe, actionAboutMeAC, actionAddComment, actionAddCommentAC, actionAddLikeComment, actionAddLikeCommentAC, actionAddLikePost, actionAddLikePostAC, actionAddPostInCollections, actionAddPostsFeedAC, actionAllPostsCount, actionAuthLogin, actionClearPromise, actionEditCommentAC, actionFindComment, actionFindCommentText, actionFindLikeComment, actionFindMyCollections, actionFindSubComment, actionFullAboutMe, actionFullLogIn, actionGetAllPosts, actionGetAvatar, actionGetFindFollowers, actionGetFindFollowing, actionGetFindLiked, actionGetPostAC, actionLoadSearchUsers, actionLoadSubscribe, actionloadUnSubscribe, actionLogIn, actionMyLikePost, actionOnLoadMyCollection, actionPending, actionPostsCount, actionPostsMyFollowing, actionProfileData, actionProfileDataAC, actionProfilePagePost, actionPromise, actionRegister, actionRejected, actionRemoveLikeComment, actionRemoveLikeCommentAC, actionRemoveLikePost, actionRemoveLikePostAC, actionRemovePostsFeedAC, actionResolved, actionSentPost, actionSetAvatar, actionSubAddComment, actionUpdateFollowers, actionUpdateFollowersAC, actionUpdateMyAvatart, actionUpdateMyFollowing, actionUpdateMyFollowingAC, actionUpdateSubCommentAC, actionUpsertAboutMe, actionUpsertCollectionAC, actionUpsertEditComment, actionUpsertLikeCommentAC, actionUpsertPost } from "../../actions";
import { queries } from "../../actions/actionQueries";
import { gql } from "../../helpers";


//*************** Promise ******************//


function* promiseWorker({ name, promise }) {
    yield put(actionPending(name))
    try {
        let data = yield promise
        yield put(actionResolved(name, data))
        return data
    }
    catch (error) {
        yield put(actionRejected(name, error))
    }
}


function* promiseWatcher() {
    yield takeEvery('PROMISE_START', promiseWorker)
}


//*************** ROUTE ******************//


function* routeWorker({ match }) {
    if (match.path in queries) {
        const { name, query, variables } = queries[match.path](match)
        const result = yield call(promiseWorker, actionPromise(name, gql(query, variables)))
        if (result) {
            yield put(actionGetPostAC({ ...result, comments: result?.comments?.reverse() }))
        }
    }
}

function* routeWatcher() {
    yield takeEvery('ROUTE', routeWorker)
}

//*************** AUTHORIZATION ******************//


function* logInWorker({ login, password, remember }) {
    const token = yield call(promiseWorker, actionLogIn(login, password))
    if (token) {
        yield put(actionAuthLogin(token, remember))
        yield put(actionFullAboutMe())
    }
}

function* fullRegisrterWorker({ login, password, remember }) {
    yield call(promiseWorker, actionRegister(login, password))
    yield put(actionFullLogIn(login, password, remember))
}

function* loginWatcher() {
    yield all([takeEvery('FULL_LOGIN', logInWorker), takeEvery('FULL_REGISTER', fullRegisrterWorker)])
}


//*************** ABOUT ME ******************//


function* aboutMeWorker() {
    const { auth } = yield select()
    if (auth?.payload?.sub?.id) {
        const taskData = yield fork(promiseWorker, actionAboutMe(auth?.payload?.sub?.id))
        const taskCollection = yield fork(promiseWorker, actionFindMyCollections(auth?.payload?.sub?.id))
        const [dataResult, collectionResult] = yield join([taskData, taskCollection])
        if (dataResult) {
            yield put(actionAboutMeAC(dataResult))
            yield put(actionUpsertCollectionAC(collectionResult))
        }
    }
}

function* aboutMeUpsertWorker({ nick, login }) {
    const { myData: { _id } } = yield select()
    const newMyData = { _id, nick, login }
    const result = yield call(promiseWorker, actionUpsertAboutMe(newMyData))
    if (result) {
        yield put(actionAboutMeAC(result))
        yield put(actionClearPromise('upsertAboutMe'))
    }
}


function* aboutMeWatcher() {
    yield all([
        takeEvery('ABOUT_ME', aboutMeWorker),
        takeEvery('ABOUT_ME_UPSERT', aboutMeUpsertWorker),
    ])
}


//*************** POSTS FEED ******************//


function* postsFeedWorker() {
    const {
        postsFeed: { posts, count },
        myData: { following, _id }
    } = yield select()
    !Array.isArray(posts) && (yield put(actionRemovePostsFeedAC()))
    const newArrFollowing = following.map(f => f._id)
    if (posts?.length !== (count ? count : 1)) {
        const taskPosts = yield fork(promiseWorker, actionPostsMyFollowing(posts?.length, [...newArrFollowing, _id]))
        const taskCount = yield fork(promiseWorker, actionPostsCount([...newArrFollowing, _id]))

        const [postsResult, countResult] = yield join([taskPosts, taskCount])

        if (postsResult && countResult) {
            yield put(actionAddPostsFeedAC(postsResult, countResult))
        }
    }
}
function* allPostsFeedWorker() {
    const {
        postsFeed: { posts, count }
    } = yield select()
    !Array.isArray(posts) && (yield put(actionRemovePostsFeedAC()))
    if (posts?.length !== (count ? count : 1)) {
        const taskPosts = yield fork(promiseWorker, actionGetAllPosts(posts?.length))
        const taskCount = yield fork(promiseWorker, actionAllPostsCount())

        const [postsResult, countResult] = yield join([taskPosts, taskCount])

        if (postsResult && countResult) {
            yield put(actionAddPostsFeedAC(postsResult, countResult))
        }
    }
}


function* postsFeedWatcher() {
    yield all([
        takeLeading('POSTS_FEED', postsFeedWorker),
        takeLeading('ALL_POSTS', allPostsFeedWorker),
    ])
}


//*************** DATA PROFILE ******************//


function* dataProfileWorker({ id }) {
    const { postsFeed: { posts, count, userData } } = yield select()
    if ((posts?.length ? posts?.length : 0) < (count ? count : 1)) {
        if (!count) {
            yield put(actionRemovePostsFeedAC())
            const taskDataProfile = yield fork(promiseWorker, actionProfileData(id))
            const taskUserPosts = yield fork(promiseWorker, actionProfilePagePost(id, (posts?.length ? posts?.length : 0)))
            const taskCountPosts = yield fork(promiseWorker, actionPostsCount([id]))

            const [dataProfile, userPosts, countPosts] = yield join([taskDataProfile, taskUserPosts, taskCountPosts])

            if (dataProfile && userPosts) {
                yield put(actionProfileDataAC(userPosts, countPosts, dataProfile))
            }
        } else {
            const userPosts = yield call(promiseWorker, actionProfilePagePost(id, posts?.length))
            if (userPosts) {
                yield put(actionProfileDataAC(userPosts, count, userData))
            }
        }
    }
}

function* dataProfileWatcher() {
    yield takeLeading('DATA_PROFILE', dataProfileWorker)
}


//*************** Find Users ******************//


function* findUserWorker({ value }) {
    yield delay(1500)
    yield call(promiseWorker, actionLoadSearchUsers(value))
}

function* findUserWatcher() {
    yield takeLatest('SEARCH_USERS', findUserWorker)
}


//*************** Like POST / COMMENT ******************//


function* addLikePostWorker({ postId }) {
    yield call(promiseWorker, actionAddLikePost(postId))
    const { likes } = yield call(promiseWorker, actionMyLikePost(postId))
    if (likes) {
        yield put(actionAddLikePostAC(postId, likes))
    }
}

function* delLikePostWorker({ likeId, postId }) {
    yield call(promiseWorker, actionRemoveLikePost(likeId))
    const { likes } = yield call(promiseWorker, actionMyLikePost(postId))
    console.log(likes);
    if (likes) {
        yield put(actionRemoveLikePostAC(postId, likes))
    }
}

function* addLikeCommentWorker({ commentId }) {
    yield call(promiseWorker, actionAddLikeComment(commentId))
    const { likes } = yield call(promiseWorker, actionFindLikeComment(commentId))
    if (likes) {
        yield put(actionUpsertLikeCommentAC(commentId, likes))
    }
}

function* delLikeCommentWorker({ likeId, commentId }) {
    yield call(promiseWorker, actionRemoveLikeComment(likeId))
    const { likes } = yield call(promiseWorker, actionFindLikeComment(commentId))
    console.log(likes);
    if (likes) {
        yield put(actionUpsertLikeCommentAC(commentId, likes))
    }
}

function* likePostWatcher() {
    yield all([
        takeEvery('LIKE_POST', addLikePostWorker),
        takeEvery('DEL_LIKE_POST', delLikePostWorker),
        takeEvery('LIKE_COMMENT', addLikeCommentWorker),
        takeEvery('DEL_LIKE_COMMENT', delLikeCommentWorker),
    ])
}


//*************** SUBSCRIBE ******************//


function* subscribeWorker({ userId }) {
    const { auth: { payload: { sub: { id } } },
        promise: { aboutMe: { payload: { following: f } } } } = yield select()

    yield call(promiseWorker, actionLoadSubscribe(id, f, userId))
    const taskFollowers = yield fork(promiseWorker, actionUpdateFollowers(userId))
    const taskMyFollowing = yield fork(promiseWorker, actionUpdateMyFollowing(id))
    const [{ followers }, { following }] = yield join([taskFollowers, taskMyFollowing])
    if (followers) {
        yield put(actionUpdateFollowersAC(followers))
        yield put(actionUpdateMyFollowingAC(following))
    }
}

function* unSubscribeWorker({ userId }) {
    const { auth: { payload: { sub: { id } } },
        promise: { aboutMe: { payload: { following: f } } } } = yield select()
    const newArrFollowing = [...f].filter(f => f._id !== userId)
    yield call(promiseWorker, actionloadUnSubscribe(id, newArrFollowing))
    const taskFollowers = yield fork(promiseWorker, actionUpdateFollowers(userId))
    const taskMyFollowing = yield fork(promiseWorker, actionUpdateMyFollowing(id))
    const [{ followers }, { following }] = yield join([taskFollowers, taskMyFollowing])
    if (followers) {
        yield put(actionUpdateFollowersAC(followers))
        yield put(actionUpdateMyFollowingAC(following))
    }
}

function* subscribeWatcher() {
    yield all([takeLeading('UN_SUBSCRIBE', unSubscribeWorker), takeLeading('SUBSCRIBE', subscribeWorker)])

}


//*************** COMMENTS ******************//


function* addCommentWorker({ text }) {
    const { postsFeed: { posts: { _id } } } = yield select()
    yield call(promiseWorker, actionAddComment(_id, text))
    const { comments } = yield call(promiseWorker, actionFindComment(_id))
    console.log(comments, _id);
    if (comments) {
        yield put(actionAddCommentAC(comments?.reverse()))
    }
}

function* editCommentWorker({ commentId, text }) {
    yield call(promiseWorker, actionUpsertEditComment(commentId, text))
    const result = yield call(promiseWorker, actionFindCommentText(commentId))
    console.log(result);
    if (commentId, result) {
        yield put(actionEditCommentAC(commentId, result))
    }

} function* addSubCommentWorker({ commentId, text }) {
    yield call(promiseWorker, actionSubAddComment(commentId, text))
    yield call(findSubCommentWorker, { commentId })
}

function* findSubCommentWorker({ commentId }) {
    const { answers } = yield call(promiseWorker, actionFindSubComment(commentId))
    if (commentId, answers) {
        yield put(actionUpdateSubCommentAC(commentId, answers))
    }
}

function* addCommentWatcher() {
    yield all([
        takeEvery('COMMENT_POST', addCommentWorker),
        takeEvery('COMMENT_EDIT', editCommentWorker),
        takeEvery('FIND_SUBCOMMENT', findSubCommentWorker),
        takeEvery('ADD_SUB_COMMENT', addSubCommentWorker)
    ])
}


//*************** UPLOAD AVATAR ******************//


function* updateAvatarWorker({ file }) {
    const { auth: { payload: { sub: { id } } } } = yield select()
    console.log(file, id);
    yield call(promiseWorker, actionSetAvatar(file, id))
    const { avatar } = yield call(promiseWorker, actionGetAvatar(id))
    console.log(avatar);
    if (avatar) {
        yield put(actionUpdateMyAvatart(avatar))
    }
}

function* updateAvatarWatcher() {
    yield takeEvery('UPDATE_AVATAR', updateAvatarWorker)
}


//*************** FIND FOLLOWING/FOLLOWERS ******************//


function* findFollowersWorker({ _id }) {
    yield call(promiseWorker, actionGetFindFollowers(_id))
}

function* findFollowingWorker({ _id }) {
    yield call(promiseWorker, actionGetFindFollowing(_id))
}

function* findLikedWorker({ _id }) {
    yield call(promiseWorker, actionGetFindLiked(_id))
}

function* findFollowWatcher() {
    yield all([
        takeEvery('FIND_FOLLOWERS', findFollowersWorker),
        takeEvery('FIND_FOLLOWING', findFollowingWorker),
        takeEvery('FIND_LIKED', findLikedWorker)
    ])
}


//*************** Create Post ******************//


function* sentPostWorker({ images, text, title }) {
    let imagesId = images.map(im => ({ _id: im._id }))
    const { postsFeed: { posts: { _id } } } = yield select()
    const upSertPostObj = {
        _id,
        images: imagesId,
        text,
        title
    }
    const result = yield call(promiseWorker, actionUpsertPost(upSertPostObj))
    if (result) {
        yield put(actionClearPromise('sentPost'))
    }
}

function* sentPostWatcher() {
    yield takeEvery('CREATE_POST', sentPostWorker)
}


//*************** COLLECTION ******************//

function* handlerCollectionWorker({ _id, flag }) {
    const { myData: { _id: myID, collections } } = yield select()
    const newArr = flag ? collections?.posts.filter(c => c._id !== _id) : [...collections?.posts || [], { _id }]
    yield call(promiseWorker, actionAddPostInCollections(collections?._id, newArr))
    const newData = yield call(promiseWorker, actionFindMyCollections(myID))
    if (newData) yield put(actionUpsertCollectionAC(newData))
}

function* loadCollectionWorker() {
    const { myData: { collections }, postsFeed } = yield select()
    !Array.isArray(postsFeed?.posts) && (yield put(actionRemovePostsFeedAC()))
    const [{ posts: newResult }] = yield call(promiseWorker, actionOnLoadMyCollection(collections?._id, postsFeed?.posts?.length))
    if (newResult) {
        yield put(actionAddPostsFeedAC(newResult))
    }

}

function* handlerCollectionWatcher() {
    yield all([
        takeEvery('HANDLER_UPSERT_COLLECTION', handlerCollectionWorker),
        takeEvery('LOAD_COLLECTION', loadCollectionWorker)
    ])
}

// 


export function* rootSaga() {
    yield all([
        promiseWatcher(),
        routeWatcher(),
        loginWatcher(),
        aboutMeWatcher(),
        postsFeedWatcher(),
        dataProfileWatcher(),
        findUserWatcher(),
        likePostWatcher(),
        subscribeWatcher(),
        addCommentWatcher(),
        updateAvatarWatcher(),
        findFollowWatcher(),
        sentPostWatcher(),
        handlerCollectionWatcher(),

    ])
}