-- | Takes an assignment, a list of reviewers and reviewees and a
-- | role. Assigns revieews to reviewers pseudorandomly until the
-- | list of revieews is exhausted. For N reviewers and M
-- | reviewees, ensures no reviewer gets less than
-- | floor (M / N) or more than ceil (M / N) reviews.
-- | Should NOT always assign more reviews to users listed
-- | at the beginning of the reviewer list.
assignReviews :: Assignment -> [UserIdentifier]
    -> [UserIdentifier]
    -> ReviewRole
    -> IO [ReviewAssignment]
assignReviews a xs ys r = do
    reviewers <- shuffleList xs
    reviews <-  shuffleList ys

    let pairs = zip (cycle reviewers) reviews
        unique = filter (uncurry (/=)) pairs
        same = map fst $ filter (uncurry (==)) pairs
        extra = zip same (tail $ cycle same)
        -- | TODO: fix for 1 reviewer [] crash
        revs = unique ++ extra

    return $ map makeRev revs
    where
        makeRev t = ReviewAssignment (fst t) (snd t) r a


-- -- | Stores a list of review assignments into a database or
-- -- | file system.
-- storeAssigments :: [ReviewAssignment] -> IO ()
-- storeAssigments xs = forM_ xs $ \x -> databaseProviderReviews $ do
--         existing <- selectList
--             [ReviewAssignmentAssignment ==. reviewAssignmentAssignment x,
--             ReviewAssignmentReviewer ==. reviewAssignmentReviewer x,
--             ReviewAssignmentReviewee ==. reviewAssignmentReviewee x,
--             ReviewAssignmentRole ==. reviewAssignmentRole x] []
--         unless (null existing) $ throw ReviewAssignmentExistsException
--         void (insert x)


-- | Stores a list of review assignments into a database or
-- | file system.
assignReviews :: Assignment -> [UserIdentifier]
    -> [UserIdentifier]
    -> ReviewRole
    -> IO [ReviewAssignment]
assignReviews a xs ys r = do
    reviewers <- shuffleList xs
    reviews <-  shuffleList ys

    let pairs = zip (cycle reviewers) reviews
        unique = filter (uncurry (/=)) pairs
        same = map fst $ filter (uncurry (==)) pairs
        extra = zip same (tail $ cycle same)
        -- | TODO: fix for 1 reviewer [] crash
        revs = unique ++ extra

    return $ map makeRev revs
    where
        makeRev t = ReviewAssignment (fst t) (snd t) r a


-- -- | Stores a list of review assignments into a database or
-- -- | file system.
-- storeAssigments :: [ReviewAssignment] -> IO ()
-- storeAssigments xs = forM_ xs $ \x -> databaseProviderReviews $ do
--         existing <- selectList
--             [ReviewAssignmentAssignment ==. reviewAssignmentAssignment x,
--             ReviewAssignmentReviewer ==. reviewAssignmentReviewer x,
--             ReviewAssignmentReviewee ==. reviewAssignmentReviewee x,
--             ReviewAssignmentRole ==. reviewAssignmentRole x] []
--         unless (null existing) $ throw ReviewAssignmentExistsException
--         void (insert x)


-- | Stores a list of review assignments into a database or
-- | file system.
assignReviews a xs ys r = do
    reviewers <- shuffleList xs
    reviews <-  shuffleList ys

    let pairs = zip (cycle reviewers) reviews
        unique = filter (uncurry (/=)) pairs
        same = map fst $ filter (uncurry (==)) pairs
        extra = zip same (tail $ cycle same)
        -- | TODO: fix for 1 reviewer [] crash
        revs = unique ++ extra

    return $ map makeRev revs
    where
        makeRev t = ReviewAssignment (fst t) (snd t) r a


-- -- | Stores a list of review assignments into a database or
-- -- | file system.
-- storeAssigments :: [ReviewAssignment] -> IO ()
-- storeAssigments xs = forM_ xs $ \x -> databaseProviderReviews $ do
--         existing <- selectList
--             [ReviewAssignmentAssignment ==. reviewAssignmentAssignment x,
--             ReviewAssignmentReviewer ==. reviewAssignmentReviewer x,
--             ReviewAssignmentReviewee ==. reviewAssignmentReviewee x,
--             ReviewAssignmentRole ==. reviewAssignmentRole x] []
--         unless (null existing) $ throw ReviewAssignmentExistsException
--         void (insert x)


-- | Stores a list of review assignments into a database or
-- | file system.