{-# LANGUAGE FlexibleInstances #-}

module Test.Data.Raft.Node where

import qualified Data.Sequence                 as Seq
import           Test.QuickCheck.Instances     ()
import           Test.Tasty.QuickCheck

import           Data.Raft.Entry
import           Data.Raft.Log
import           Data.Raft.Node
import           Data.Raft.NodeIdentifier
import           Data.Raft.Standing
import           Data.Raft.Term
import           Data.RaftPrelude
import           Test.Data.Raft.Log
import           Test.Data.Raft.NodeIdentifier
import           Test.Data.Raft.Standing

type IntNode = Node SelfId CandidateId LeaderId

instance Arbitrary (Node Int Int Int) where
  arbitrary = genericNode

genericNode :: Gen IntNode
genericNode = Node
  <$> arbitrary
  <*> arbitrary
  <*> arbitrary

genericFollowerNode :: Gen IntNode
genericFollowerNode = Node
  <$> genericFollowerStanding
  <*> arbitrary
  <*> arbitrary

genericCandidateNode :: Gen IntNode
genericCandidateNode = Node
  <$> genericCandidateStanding
  <*> arbitrary
  <*> arbitrary

genericLeaderNode :: Gen IntNode
genericLeaderNode = Node
  <$> genericLeaderStanding
  <*> arbitrary
  <*> arbitrary

genericNonLeaderNode :: Gen IntNode
genericNonLeaderNode = oneof
  [ genericFollowerNode
  , genericCandidateNode
  ]

followerNodeAtTerm :: Term -> Gen IntNode
followerNodeAtTerm term = Node
  <$> followerStandingAtTerm term
  <*> arbitrary
  <*> arbitrary

followerNodeSupporting :: NodeIdentifier Int -> Gen IntNode
followerNodeSupporting ident = Node
  <$> followerStandingSupporting ident
  <*> arbitrary
  <*> arbitrary

followerNodeSupportingArbitrary :: Gen IntNode
followerNodeSupportingArbitrary = followerNodeSupporting =<< arbitrary

followerNodeSupportingNoneWithMinimumLog :: Int -> Gen IntNode
followerNodeSupportingNoneWithMinimumLog minLogSize = do
  log'@(Log s _) <- scale (\s -> (s + 1) * minLogSize) genericLog
  let term = case Seq.viewr s of
               EmptyR     -> error "Log was constructed with no values."
               _ :> last' -> entryTerm last'
  Node
    <$> followerStandingAtTerm term
    <*> arbitrary
    <*> pure log'

followerNodeWithExpiredTimeout :: Gen (IntNode, CurrentTime)
followerNodeWithExpiredTimeout = do
  (stand, ctime) <- followerStandingWithExpiredTimeout
  st <- arbitrary
  log' <- arbitrary
  return (Node stand st log', ctime)

candidateNodeWithExpiredTimeout :: Gen (IntNode, CurrentTime)
candidateNodeWithExpiredTimeout = do
  (stand, ctime) <- candidateStandingWithExpiredTimeout
  st <- arbitrary
  log' <- arbitrary
  return (Node stand st log', ctime)

leaderNodeWithExpiredTimeout :: Gen (IntNode, CurrentTime)
leaderNodeWithExpiredTimeout = do
  (stand, ctime) <- leaderStandingWithExpiredTimeout
  st <- arbitrary
  log' <- arbitrary
  return (Node stand st log', ctime)

