{-# LANGUAGE DerivingVia, DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Data.Aeson
import Deriving.Aeson
import Deriving.Aeson.Stock
import System.Exit (die)
import qualified Data.ByteString.Lazy.Char8 as BL


data User = User
  { userId :: Int
  , userName :: String
  , userAPIToken :: Maybe String
  , userType :: String
  } deriving Generic
  deriving (FromJSON, ToJSON)
  via CustomJSON '[ OmitNothingFields
                  , FieldLabelModifier '[StripPrefix "user", CamelToSnake, Rename "type" "user_type"]
                  ] User

data Foo = Foo { fooFoo :: Int, fooBar :: Int }
  deriving Generic
  deriving (FromJSON, ToJSON)
  via Prefixed "foo" Foo

testData :: [User]
testData = [User 42 "Alice" Nothing "human", User 43 "Bob" (Just "xyz") "bot"]

data MultipleCtorRenames
  = RenamedCtorOptA
  | RenamedCtorOptB (Maybe ())
  | RenamedCtorOptC Char
  deriving (Eq, Generic, Show)
  deriving (ToJSON)
    via CustomJSON
      [ ConstructorTagModifier (Rename "RenamedCtorOptA" "nullary")
      , ConstructorTagModifier (Rename "RenamedCtorOptB" "twisted-bool")
      , ConstructorTagModifier (Rename "RenamedCtorOptC" "wrapped-char")
      ] MultipleCtorRenames

data MultipleFieldRenames = MultipleFieldRenames
  { fooField1 :: Int
  , fooField2 :: Bool
  , fooField3 :: String
  }
  deriving (Eq, Generic, Show)
  deriving (ToJSON)
    via CustomJSON
      [ FieldLabelModifier (Rename "fooField1" "field-1")
      , FieldLabelModifier (Rename "fooField2" "field-2")
      , FieldLabelModifier (Rename "fooField3" "field-3")
      ] MultipleFieldRenames

main = do
  BL.putStrLn $ encode testData
  BL.putStrLn $ encode $ Foo 0 1

  assertEq
    (toJSON RenamedCtorOptA)
    (object [("tag", "nullary")])
    "Support multiple constructor modifiers"

  assertEq
    (toJSON $ RenamedCtorOptB Nothing)
    (object [("tag", String "twisted-bool"), ("contents", Null)])
    "Support multiple constructor modifiers"

  assertEq
    (toJSON $ RenamedCtorOptC '?')
    (object [("tag", String "wrapped-char"), ("contents", String "?")])
    "Support multiple constructor modifiers"

  assertEq
    (toJSON $ MultipleFieldRenames 42 True "meaning of life")
    (object [("field-1", Number 42)
            ,("field-2", Bool True)
            ,("field-3", String "meaning of life")
            ])
    "Support multiple field modifiers"

assertEq :: (Show a, Eq a) => a -> a -> String -> IO ()
assertEq x y expectation | x == y = pure ()
                         | otherwise = die msg
  where
    msg = concat [expectation, " -- not fulfilled:\n\t", show x, "\n\t /= \n\t", show y]
