{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module SafeAeson where


import Data.Aeson (Value)
import qualified Data.Aeson as A
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import System.IO.Temp (withTempDirectory)
import Test.Tasty
import Test.Tasty.HUnit

import Data.Aeson.Safe
import Types


encodeDecodeTests :: TestTree
encodeDecodeTests = testGroup "Encoding/Decoding (Data.Aeson.Safe)"
    [ parseFile   @NoVersion "decodeFile  (noversion)" noVersionPath
    , parseFile'  @NoVersion "decodeFile' (noversion)" noVersionPath
    , parseBigFile           "decodeFile (primitives)" primitivesPath
    , writeToFile            "encodeFile"
    , parseValue  @NoVersion "decode      (noversion)"
    , parseValue' @NoVersion "decode'     (noversion)"
    , encodeType             "encode"
    ]


jsonPath :: FilePath
jsonPath = "test/json"

noVersionPath :: FilePath
noVersionPath = jsonPath ++ "/noversion.json"

primitivesPath :: FilePath
primitivesPath = jsonPath ++ "/primitives.json"

noVersionType :: NoVersion
noVersionType = NoVersion 23986

parseBigFile :: String -> FilePath -> TestTree
parseBigFile s fp = testCase s $ do
    a <- decodeFileStrict @Value fp
    b <- A.decodeFileStrict fp
    b @=? a
    c <- decodeFileStrict' @Value fp
    d <- A.decodeFileStrict' fp
    d @=? c
    e <- eitherDecodeFileStrict @Value fp
    x <- A.eitherDecodeFileStrict fp
    x @=? e
    y <- eitherDecodeFileStrict' @Value fp
    z <- A.eitherDecodeFileStrict' fp
    z @=? y

parseValue :: forall a. (FromJSON a, SafeJSON a, Eq a, Show a) => String -> TestTree
parseValue s = testCase s $ do
    lbs <- LBS.readFile noVersionPath
    decode @a       lbs @=? A.decode       lbs
    eitherDecode @a lbs @=? A.eitherDecode lbs

    bs <- BS.readFile noVersionPath
    decodeStrict @a       bs @=? A.decodeStrict       bs
    eitherDecodeStrict @a bs @=? A.eitherDecodeStrict bs

parseValue' :: forall a. (FromJSON a, SafeJSON a, Eq a, Show a) => String -> TestTree
parseValue' s = testCase s $ do
    lbs <- LBS.readFile noVersionPath
    decode' @a       lbs @=? A.decode'       lbs
    eitherDecode' @a lbs @=? A.eitherDecode' lbs

    bs <- BS.readFile noVersionPath
    decodeStrict' @a       bs @=? A.decodeStrict'       bs
    eitherDecodeStrict' @a bs @=? A.eitherDecodeStrict' bs

parseFile :: forall a. (FromJSON a, SafeJSON a, Eq a, Show a) => String -> FilePath -> TestTree
parseFile s fp = testCase s $ do
    a <- decodeFileStrict @Value fp
    b <- A.decodeFileStrict fp
    b @=? a

    c <- decodeFileStrict @a fp
    d <- A.decodeFileStrict fp
    d @=? c

    e <- eitherDecodeFileStrict @Value fp
    x <- A.eitherDecodeFileStrict fp
    x @=? e

    y <- eitherDecodeFileStrict @a fp
    z <- A.eitherDecodeFileStrict fp
    z @=? y

parseFile' :: forall a. (FromJSON a, SafeJSON a, Eq a, Show a) => String -> FilePath -> TestTree
parseFile' s fp = testCase s $ do
    a <- decodeFileStrict' @Value fp
    b <- A.decodeFileStrict' fp
    b @=? a

    c <- decodeFileStrict' @a fp
    d <- A.decodeFileStrict' fp
    d @=? c

    e <- eitherDecodeFileStrict' @Value fp
    x <- A.eitherDecodeFileStrict' fp
    x @=? e

    y <- eitherDecodeFileStrict' @a fp
    z <- A.eitherDecodeFileStrict' fp
    z @=? y

writeToFile :: String -> TestTree
writeToFile s = testCase s $ do
    withTempDirectory "test/json" "encode" $ \dir -> do
        let file1 = dir ++ "/test.json"
            file2 = dir ++ "/test2.json"
        encodeFile file1 noVersionType
        A.encodeFile file2 noVersionType
        x <- A.decodeFileStrict @NoVersion file1
        y <- A.decodeFileStrict file2
        y @=? x

encodeType :: String -> TestTree
encodeType s = testCase s $
    encode noVersionType @=? A.encode noVersionType
