module LabeledTests (labeledTests) where

import Test.Tasty
import Test.Tasty.HUnit

import Effectful
import Effectful.Dispatch.Dynamic
import Effectful.Labeled
import Effectful.Reader.Static
import Utils qualified as U

labeledTests :: TestTree
labeledTests = testGroup "Labeled"
  [ testCase "labeled behaves correctly" $ test_labeledBehavior
  , testCase "(labeled . send) and (send . Labeled) behave the same" $ test_labeledSend
  ]

test_labeledBehavior :: Assertion
test_labeledBehavior = do
  v <- runEff
    . runLabeled @"a" (runReader "a")
    . runLabeled @"b" (runReader "b")
    . runReader "c"
    $ action
  assertEqual "expected result" "abc" v
  where
    action
      :: ( Labeled "a" (Reader String) :> es
         , Labeled "b" (Reader String) :> es
         , Reader String :> es
         )
      => Eff es String
    action = do
      a <- labeled @"b" @(Reader String) $ do
        labeled @"a" @(Reader String) $ do
          ask
      b <- labeled @"b" @(Reader String) $ do
        ask
      c <- ask
      pure $ a ++ b ++ c

test_labeledSend :: Assertion
test_labeledSend = runEff $ do
  runX 1 . runLabeled @"x" (runX 2) $ do
    v0 <- send X2
    U.assertEqual "expected result" 1 v0
    v1 <- (labeled @"x" @X . send) X2
    U.assertEqual "expected result" 2 v1
    v2 <- (send . Labeled @"x") X2
    U.assertEqual "expected result" 2 v2

data X :: Effect where
  X1 :: X m Int
  X2 :: (X :> es, Labeled "x" X :> es) => X (Eff es) Int

type instance DispatchOf X = Dynamic

runX :: Int -> Eff (X : es) a -> Eff es a
runX x = interpret $ \env -> \case
  X1 -> pure x
  X2 -> localSeqUnlift env $ \unlift -> unlift $ send X1
