# LANGUAGE OverloadedLists    #
{-# LANGUAGE OverloadedStrings #-}
module Language.Haskell.Stylish.Step.Data.Tests
    ( tests
    ) where

import           Language.Haskell.Stylish.Step.Data
import           Language.Haskell.Stylish.Tests.Util (assertSnippet)
import           Test.Framework                      (Test, testGroup)
import           Test.Framework.Providers.HUnit      (testCase)
import           Test.HUnit                          (Assertion)

tests :: Test
tests = testGroup "Language.Haskell.Stylish.Step.Data.Tests"
    [ testCase "case 00" case00
    , testCase "case 01" case01
    , testCase "case 02" case02
    , testCase "case 03" case03
    , testCase "case 04" case04
    , testCase "case 05" case05
    , testCase "case 06" case06
    , testCase "case 07" case07
    , testCase "case 08" case08
    , testCase "case 09" case09
    , testCase "case 10" case10
    , testCase "case 11" case11
    , testCase "case 12" case12
    , testCase "case 13" case13
    , testCase "case 14" case14
    , testCase "case 15" case15
    , testCase "case 16" case16
    , testCase "case 17" case17
    , testCase "case 18" case18
    , testCase "case 19" case19
    , testCase "case 20 (issue #262)" case20
    , testCase "case 21" case21
    , testCase "case 22" case22
    , testCase "case 23" case23
    , testCase "case 24" case24
    , testCase "case 25" case25
    , testCase "case 26" case26
    , testCase "case 27" case27
    , testCase "case 28" case28
    , testCase "case 29" case29
    , testCase "case 30" case30
    , testCase "case 31" case31
    , testCase "case 32" case32
    , testCase "case 33" case33
    , testCase "case 34" case34
    , testCase "case 35" case35
    , testCase "case 36" case36
    , testCase "case 37" case37
    , testCase "case 38" case38
    , testCase "case 39" case39
    , testCase "case 40" case40
    , testCase "case 41" case41
    , testCase "case 42" case42
    , testCase "case 43" case43
    , testCase "case 44" case44
    , testCase "case 45" case45
    , testCase "case 46" case46
    , testCase "case 47" case47
    , testCase "case 48" case48
    , testCase "case 49" case49
    , testCase "case 50" case50
    , testCase "case 51" case51
    , testCase "case 52" case52
    , testCase "case 53" case53
    , testCase "case 54" case54
    , testCase "case 55" case55
    , testCase "case 56" case56
    , testCase "case 57" case57
    , testCase "case 58" case58
    , testCase "case 59" case59
    , testCase "case 60" case60
    , testCase "case 61 (issue #282)" case61
    , testCase "case 62 (issue #273)" case62
    , testCase "case 63 (issue #338)" case63
    , testCase "case 64" case64
    , testCase "case 65" case65
    , testCase "case 66 (issue #411)" case66
    ]

case00 :: Assertion
case00 = assertSnippet (step sameSameStyle) input input
  where
    input =
        [ "module Herp where"
        , ""
        , "data Foo"
        ]

case01 :: Assertion
case01 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo { a :: Int }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "      }"
    ]

case02 :: Assertion
case02 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo { a :: Int, a2 :: String }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "      , a2 :: String"
    , "      }"
    ]

case03 :: Assertion
case03 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo { a :: a, a2 :: String }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "      , a2 :: String"
    , "      }"
    ]

case04 :: Assertion
case04 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo { a :: a, a2 :: String } | Bar { b :: a }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "      , a2 :: String"
    , "      }"
    , "  | Bar"
    , "      { b :: a"
    , "      }"
    ]

case05 :: Assertion
case05 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo {"
    , "  a :: Int"
    , "  , a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "      , a2 :: String"
    , "      }"
    ]

case06 :: Assertion
case06 = assertSnippet (step sameSameStyle) input input
  where
    input =
        [ "module Herp where"
        , ""
        , "data Foo = Foo Int String"
        ]

case07 :: Assertion
case07 = assertSnippet (step sameSameStyle) input input
  where
    input =
        [ "module Herp where"
        , ""
        , "data Phantom a = Phantom"
        ]

case08 :: Assertion
case08 = assertSnippet (step sameSameStyle)
    [ "module Herp where"
    , ""
    , "data Phantom a ="
    , "  Phantom"
    ]
    [ "module Herp where"
    , ""
    , "data Phantom a = Phantom"
    ]

case09 :: Assertion
case09 = assertSnippet (step indentIndentStyle4)
    [ "module Herp where"
    , ""
    , "data Foo a b = Foo { a :: a, a2 :: String } | Bar { b :: a, c:: b }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a b"
    , "    = Foo"
    , "          { a :: a"
    , "          , a2 :: String"
    , "          }"
    , "    | Bar"
    , "          { b :: a"
    , "          , c :: b"
    , "          }"
    ]

case10 :: Assertion
case10 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo = Foo { a :: Int } deriving (Eq, Generic) deriving (Show)"
      ]

    expected =
       [ "module Herp where"
       , ""
       , "data Foo"
       , "  = Foo"
       , "      { a :: Int"
       , "      }"
       , "  deriving (Eq, Generic)"
       , "  deriving (Show)"
       ]

case11 :: Assertion
case11 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "{-# LANGUAGE DerivingStrategies #-}"
      , "module Herp where"
      , ""
      , "data Foo = Foo { a :: Int } deriving stock (Show)"
      ]

    expected =
       [ "{-# LANGUAGE DerivingStrategies #-}"
       , "module Herp where"
       , ""
       , "data Foo"
       , "  = Foo"
       , "      { a :: Int"
       , "      }"
       , "  deriving stock (Show)"
       ]

case12 :: Assertion
case12 = assertSnippet (step indentIndentStyle4) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Point = Point { pointX, pointY :: Double , pointName :: String} deriving (Show)"
      ]

    expected =
       [ "module Herp where"
       , ""
       , "data Point"
       , "    = Point"
       , "          { pointX, pointY :: Double"
       , "          , pointName :: String"
       , "          }"
       , "    deriving (Show)"
       ]

case13 :: Assertion
case13 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "-- this is a comment"
      , "data Foo = Foo { a :: Int }"
      ]
    expected =
      [ "module Herp where"
      , ""
      , "-- this is a comment"
      , "data Foo"
      , "  = Foo"
      , "      { a :: Int"
      , "      }"
      ]

case14 :: Assertion
case14 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "{- this is"
      , "   a comment -}"
      , "data Foo = Foo { a :: Int }"
      ]
    expected =
      [ "module Herp where"
      , ""
      , "{- this is"
      , "   a comment -}"
      , "data Foo"
      , "  = Foo"
      , "      { a :: Int"
      , "      }"
      ]

case15 :: Assertion
case15 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { a :: a, -- comment"
    , "   a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "        -- comment"
    , "      , a2 :: String"
    , "      }"
    ]

case16 :: Assertion
case16 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo {"
    , "   a :: Int -- ^ comment"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "        -- ^ comment"
    , "      }"
    ]

case17 :: Assertion
case17 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { a :: a,"
    , "-- comment"
    , "   a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "        -- comment"
    , "      , a2 :: String"
    , "      }"
    ]

case18 :: Assertion
case18 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { a :: a,"
    , "-- ^ comment"
    , "   a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "        -- ^ comment"
    , "      , a2 :: String"
    , "      }"
    ]

case19 :: Assertion
case19 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { firstName, lastName :: String,"
    , "-- ^ names"
    , "   age :: Int"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { firstName, lastName :: String"
    , "        -- ^ names"
    , "      , age :: Int"
    , "      }"
    ]

  | Should not break Enums ( data without records ) formatting
--
-- See -haskell/issues/262
case20 :: Assertion
case20 = assertSnippet (step indentIndentStyle) input input
  where
    input =
       [ "module Herp where"
       , ""
       , "data Tag = Title | Text"
       , "  deriving (Eq, Show)"
       ]

case21 :: Assertion
case21 = assertSnippet (step sameSameStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a = Foo { a :: Int"
    , "                 , a2 :: String"
    , "                   -- ^ some haddock"
    , "                 }"
    , "           | Bar { b :: a"
    , "                 }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case22 :: Assertion
case22 = assertSnippet (step sameIndentStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a = Foo"
    , "               { a :: Int"
    , "               , a2 :: String"
    , "                 -- ^ some haddock"
    , "               }"
    , "           | Bar"
    , "               { b :: a"
    , "               }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case23 :: Assertion
case23 = assertSnippet (step indentSameStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a"
    , "  = Foo { a :: Int"
    , "        , a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a"
    , "        }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case24 :: Assertion
case24 = assertSnippet (step indentIndentStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a"
    , "  = Foo"
    , "      { a :: Int"
    , "      , a2 :: String"
    , "        -- ^ some haddock"
    , "      }"
    , "  | Bar"
    , "      { b :: a"
    , "      }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case25 :: Assertion
case25 = assertSnippet (step indentIndentStyle { cBreakSingleConstructors = False }) input expected
  where
    input =
       [ "data Foo a"
       , "  = Foo { a :: Int,"
       , "          a2 :: String"
       , "          -- ^ some haddock"
       , "        }"
       , "  deriving (Eq, Show)"
       , "  deriving (ToJSON)"
       ]

    expected =
       [ "data Foo a = Foo"
       , "  { a :: Int"
       , "  , a2 :: String"
       , "    -- ^ some haddock"
       , "  }"
       , "  deriving (Eq, Show)"
       , "  deriving (ToJSON)"
       ]

case26 :: Assertion
case26 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo = Foo { a :: Int } deriving (FromJSON) via Bla Foo"
      ]

    expected =
       [ "module Herp where"
       , ""
       , "data Foo"
       , "  = Foo"
       , "      { a :: Int"
       , "      }"
       , "  deriving (FromJSON) via Bla Foo"
       ]

case27 :: Assertion
case27 = assertSnippet (step sameIndentStyle { cBreakEnums = True }) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo = Foo | Bar | Baz deriving (Eq, Show)"
      ]

    expected =
      [ "module Herp where"
      , ""
      , "data Foo"
      , "  = Foo"
      , "  | Bar"
      , "  | Baz"
      , "  deriving (Eq, Show)"
      ]

case28 :: Assertion
case28 = assertSnippet (step sameIndentStyle { cBreakEnums = True }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype BankCode = BankCode {"
      , "    unBankCode :: Text"
      , "  }"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype CheckDigit = CheckDigit { unCheckDigit :: Text }"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype WrappedInt = WrappedInt Int"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "data MandateStatus"
      , "  = Approved"
      , "  | Failed"
      , "  | UserCanceled"
      , "  | Inactive"
      , "  deriving stock (Generic, Show, Eq, Enum, Bounded)"
      , "  deriving (ToJSON, FromJSON) via SnakeCaseCapsEnumEncoding MandateStatus"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype BankCode = BankCode { unBankCode :: Text }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype CheckDigit = CheckDigit { unCheckDigit :: Text }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype WrappedInt = WrappedInt Int"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "data MandateStatus"
      , "  = Approved"
      , "  | Failed"
      , "  | UserCanceled"
      , "  | Inactive"
      , "  deriving stock (Bounded, Enum, Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON) via SnakeCaseCapsEnumEncoding MandateStatus"
      ]

case29 :: Assertion
case29 = assertSnippet (step sameIndentStyle) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "data NonEmpty a"
      , "  = a :| [a]"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "data NonEmpty a = a :| [a]"
      ]

case30 :: Assertion
case30 = assertSnippet (step sameIndentStyle { cBreakEnums = True }) input expected
  where
    expected = input
    input =
      [ "data ReasonCode"
      , "  = MissingTenantId"
      , "  -- Transaction errors:"
      , "  | TransactionDoesNotExist"
      , "  | TransactionAlreadyExists"
      , "  -- Engine errors:"
      , "  | EnginePersistenceError"
      , "  | EngineValidationError"
      , "  -- | Transaction was created in Info mode"
      , "  | RegisteredByNetworkEngine"
      , "  -- | Transaction was created in Routing mode"
      , "  | SentToNetworkEngine"
      , "  -- Network connection reasons:"
      , "  | SentToNetworkConnection"
      , "  | ReceivedByNetworkConnection"
      , "  | ValidatedByNetworkConnection"
      ]


case31 :: Assertion
case31 = assertSnippet (step indentIndentStyle { cBreakEnums = True }) input expected
  where
    expected = input
    input =
      [ "data ConfiguredLogger"
      , "  -- | Logs to file"
      , "  = LogTo FilePath"
      , "  -- | Logs to stdout"
      , "  | LogToConsole"
      , "  -- | No logging, discards all messages"
      , "  | NoLogging"
      , "  deriving stock (Generic, Show)"
      ]

case32 :: Assertion
case32 = assertSnippet (step indentIndentStyle { cBreakEnums = True }) input expected
  where
    expected = input
    input =
      [ "data RejectionReason"
      , "  -- InvalidState"
      , "  = CancellationFailed"
      , "  | TotalAmountConfirmationInvalid"
      , "  -- InvalidApiUsage"
      , "  | AccessTokenNotActive"
      , "  | VersionNotFound"
      , "  -- ValidationFailed"
      , "  | BankAccountExists"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON) via SnakeCaseLowercaseEnumEncoding RejectionReason"
      ]

case33 :: Assertion
case33 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a = NonEmpty { unNonEmpty :: a }"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a"
      , "  = NonEmpty { unNonEmpty :: a }"
      ]

case34 :: Assertion
case34 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a = NonEmpty { unNonEmpty :: a }"
      , "     deriving (ToJSON, FromJSON) via Something Magic (NonEmpty a)"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a"
      , "  = NonEmpty { unNonEmpty :: a }"
      , "  deriving (FromJSON, ToJSON)"
      , "    via Something Magic (NonEmpty a)"
      ]

case35 :: Assertion
case35 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: MonetaryAmount"
      , "      -> TransactionId"
      , "      -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: MonetaryAmount -> TransactionId -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

case36 :: Assertion
case36 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: (a -> b)"
      , "      -> TransactionId"
      , "      -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: (a -> b) -> TransactionId -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

case37 :: Assertion
case37 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype UndoFlowData"
      , "  = UndoFlowData { flowDataDetails :: FlowDataDetails }"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving (ToJSON, FromJSON)"
      , "    via AddConstTextFields '[\"type0\" := \"undo\","
      , "                             \"type1\" := \"undo\","
      , "                     \"reversal_indicator\" := \"Undo\"] FlowDataDetails"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype UndoFlowData"
      , "  = UndoFlowData { flowDataDetails :: FlowDataDetails }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via AddConstTextFields '[\"type0\" := \"undo\", \"type1\" := \"undo\", \"reversal_indicator\" := \"Undo\"] FlowDataDetails"
      ]

case38 :: Assertion
case38 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "data Flat = Flat"
      , "  { foo :: Int"
      , "  , bar :: Text"
      , "  , baz :: Double"
      , "  , qux :: Bool"
      , "  }"
      , "  deriving stock (Generic, Show, Eq)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via GenericEncoded"
      , "      '[ FieldLabelModifier :="
      , "        '[ \"foo\" ==> \"nestFoo#foo\""
      , "         , \"bar\" ==> \"nestBar#bar\""
      , "         , \"baz\" ==> \"nestFoo#baz\""
      , "         ]"
      , "       ]"
      , "      Flat"
      ]

    expected =
      [ "data Flat"
      , "  = Flat"
      , "      { foo :: Int"
      , "      , bar :: Text"
      , "      , baz :: Double"
      , "      , qux :: Bool"
      , "      }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via GenericEncoded '[FieldLabelModifier := '[\"foo\" ==> \"nestFoo#foo\", \"bar\" ==> \"nestBar#bar\", \"baz\" ==> \"nestFoo#baz\"]] Flat"
      ]

case39 :: Assertion
case39 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "data CreditTransfer = CreditTransfer"
      , "  { nestedCreditorInfo :: CreditorInfo"
      , "  }"
      , "  deriving stock (Show, Eq, Generic)"
      , "  deriving (ToJSON, FromJSON) via"
      , "    ( UntaggedEncoded NordeaCreditTransfer"
      , "    & AddConstTextFields"
      , "      '[ \"request_type\" ':= \"credit_transfer\""
      , "       , \"provider\" ':= \"nordea\""
      , "       ]"
      , "    & FlattenFields '[\"nested_creditor_info\"]"
      , "    & RenameKeys"
      , "        '[ \"nested_creditor_info.creditor_agent_bic\" ==> \"creditor_agent_bic\""
      , "         , \"nested_creditor_info.creditor_iban\" ==> \"creditor_iban\""
      , "         , \"nested_creditor_info.creditor_name\" ==> \"creditor_name\""
      , "         , \"nested_creditor_info.creditor_account\" ==> \"creditor_account\""
      , "         ]"
      , "    )"
      ]

    expected =
      [ "data CreditTransfer"
      , "  = CreditTransfer"
      , "      { nestedCreditorInfo :: CreditorInfo"
      , "      }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via (UntaggedEncoded NordeaCreditTransfer & AddConstTextFields '[\"request_type\" ':= \"credit_transfer\", \"provider\" ':= \"nordea\"] & FlattenFields '[\"nested_creditor_info\"] & RenameKeys '[\"nested_creditor_info.creditor_agent_bic\" ==> \"creditor_agent_bic\", \"nested_creditor_info.creditor_iban\" ==> \"creditor_iban\", \"nested_creditor_info.creditor_name\" ==> \"creditor_name\", \"nested_creditor_info.creditor_account\" ==> \"creditor_account\"])"
      ]

case40 :: Assertion
case40 = assertSnippet (step indentIndentStyle { cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module X where"
      , ""
      , "data a :==> b    ="
      , "                  Arr a b"
      ]

    expected =
      [ "module X where"
      , ""
      , "data a :==> b = Arr a b"
      ]

case41 :: Assertion
case41 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected

    expected =
      [ "module X where"
      , ""
      , "data Callback"
      , "  -- | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
      , "  --   incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis"
      , "  --   nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
      , "  --   Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore"
      , "  --   eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,"
      , "  --   sunt in culpa qui officia deserunt mollit anim id est laborum."
      , "  = KafkaTopic"
      , "      { callbackTopic :: CallbackTopic"
      , "        -- ^ Name of topic to send updates to"
      , "      , callbackFormat :: CallbackFormat"
      , "        -- ^ The format used to send these updates"
      , "      }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON) via IdiomaticWithDescription CallbackDesc Callback"
      , "  deriving (HasGen) via Generically Callback"
      , "  deriving (FromField) via JsonField Callback"
      ]

case42 :: Assertion
case42 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected

    expected =
      [ "module X where"
      , ""
      , "data SignupError"
      , "  = IdempotencyConflict"
      , "  | ValidationError Text -- TODO: might be a sumtype of possible error codes"
      , "  deriving stock (Eq, Generic, Show)"
      ]

case43 :: Assertion
case43 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input = expected

    expected =
      [ "module X where"
      , ""
      , "data CallbackResult"
      , "  -- | Callback successfully sent"
      , "  = Success"
      , "  -- | Kafka error received"
      , "  | KafkaIssue KafkaError"
      , "  deriving (Eq, Show)"
      ]

-- This test showcases a difficult to solve issue. If the comment is in a
-- deriving clause, it's very hard to guess the correct position of the entire
-- block. E.g. the deriving clause itself has the wrong position. However, if
-- we look at all deriving clauses we know where they start and end.
--
-- This means that we've needed to make the decision to put all inline comments
-- before the deriving clause itself
case44 :: Assertion
case44 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False, cVia = Indent 2 }) input expected
  where
    input =
      [ "module X where"
      , ""
      , " data CreditTransfer = CreditTransfer"
      , "   { amount :: Amount -- ^ 1 <= amount <= 999_999_999_999"
      , "   , date :: Day"
      , "   , accountNumber :: Account"
      , "   }"
      , "   deriving stock (Eq, Generic, Show)"
      , "   deriving (FromJSON, ToJSON) via"
      , "     AddConstTextFields"
      , "       '[\"notification_type\" ':= \"credit_transaction\""
      , "         -- Note that the bcio name has \"transaction\""
      , "         -- rather than \"transfer\""
      , "        ]"
      , "        (UntaggedEncoded CreditTransfer)"
      ]
    expected =
      [ "module X where"
      , ""
      , "data CreditTransfer = CreditTransfer"
      , "  { amount :: Amount"
      , "    -- ^ 1 <= amount <= 999_999_999_999"
      , "  , date :: Day"
      , "  , accountNumber :: Account"
      , "  }"
      , "  -- Note that the bcio name has \"transaction\""
      , "  -- rather than \"transfer\""
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via AddConstTextFields '[\"notification_type\" ':= \"credit_transaction\"] (UntaggedEncoded CreditTransfer)"
      ]

case45 :: Assertion
case45 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False, cVia = Indent 2 }) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "data CreditTransfer = CreditTransfer"
      , "  { amount :: Amount"
      , "    -- ^ 1 <= amount <= 999_999_999_999"
      , "  , date :: Day"
      , "  , accountNumber :: Account"
      , "  }"
      , "  -- Note that the bcio name has \"transaction\""
      , "  -- rather than \"transfer\""
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via AddConstTextFields '[\"notification_type\" ':= \"credit_transaction\"] (UntaggedEncoded CreditTransfer)"
      ]

case46 :: Assertion
case46 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False, cVia = Indent 2 }) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A format detailing which encoding to use for the settlement events"
      , "data CallbackFormat"
      , "  -- | The Avro schema is to be used"
      , "  = AvroEngineEvent"
      , "  deriving (Bounded, Enum, Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via TypeTaggedWithDescription FormatDesc CallbackFormat"
      , "  deriving (HasGen)"
      , "    via EnumBounded CallbackFormat"
      ]

case47 :: Assertion
case47 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: (a, a) -> T [a]"
      ]

case48 :: Assertion
case48 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. (Eq a, Bounded a) => (a, a) -> T [a]"
      ]

case49 :: Assertion
case49 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. (Eq a) => (a, a) -> T [a]"
      ]

case50 :: Assertion
case50 = assertSnippet (step indentIndentStyle { cCurriedContext = True }) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. Eq a => (a, a) -> T [a]"
      ]

case51 :: Assertion
case51 = assertSnippet (step indentIndentStyle { cCurriedContext = True }) input expected
  where
    input =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. (Eq a) => (a, a) -> T [a]"
      ]
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. Eq a => (a, a) -> T [a]"
      ]

case52 :: Assertion
case52 = assertSnippet (step indentIndentStyle { cBreakSingleConstructors = False, cCurriedContext = True }) input expected
  where
    input =
      [ "module X where"
      , ""
      , "data Foo = Foo"
      , "  { foo :: forall a b. (Eq a, Bounded b) => a -> b -> [(a, b)]"
      , "  }"
      ]
    expected =
      [ "module X where"
      , ""
      , "data Foo = Foo"
      , "  { foo :: forall a b. Eq a => Bounded b => a -> b -> [(a, b)]"
      , "  }"
      ]

case53 :: Assertion
case53 = assertSnippet (step indentIndentStyle { cMaxColumns = MaxColumns 80 }) input expected
  where
    input =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype (Functor, Applicative, Monad, MonadError, MonadCatch, Foldable, Monoid)"
      ]
    expected =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype"
      , "    ( Applicative"
      , "    , Foldable"
      , "    , Functor"
      , "    , Monad"
      , "    , MonadCatch"
      , "    , MonadError"
      , "    , Monoid"
      , "    )"
      ]

case54 :: Assertion
case54 = assertSnippet (step indentIndentStyle { cMaxColumns = MaxColumns 80 }) input expected
  where
    input =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype (Functor, Applicative, Monad)"
      ]
    expected =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype (Applicative, Functor, Monad)"
      ]

case55 :: Assertion
case55 = assertSnippet (step sameSameNoSortStyle) input expected
  where
    input =
       [ "data Foo = Foo"
       , "  deriving (Z, Y, X, Bar, Abcd)"
       ]

    expected = input

case56 :: Assertion
case56 = assertSnippet (step defaultConfig)
    [ "data Foo = Foo"
    , "  { -- | Comment"
    , "    bar :: Int"
    , "  , baz :: Int"
    , "  }"
    ]
    [ "data Foo = Foo"
    , "    { -- | Comment"
    , "      bar :: Int"
    , "    , baz :: Int"
    , "    }"
    ]

case57 :: Assertion
case57 = assertSnippet (step defaultConfig)
    [ "data Foo = Foo"
    , "    { {- | A"
    , "      -}"
    , "      fooA :: Int"
    , ""
    , "      {- | B"
    , "      -}"
    , "    , fooB :: Int"
    , ""
    , "      {- | C"
    , "      -}"
    , "    , fooC :: Int"
    , ""
    , "      {- | D"
    , "      -}"
    , "    , fooD :: Int"
    , ""
    , "      {- | E"
    , "      -}"
    , "    , fooE :: Int"
    , ""
    , "      {- | F"
    , "      -}"
    , "    , fooFooFoo :: Int"
    , ""
    , "      {- | G"
    , "      -}"
    , "    , fooBarBar :: Int"
    , "    }"
    ]
    [ "data Foo = Foo"
    , "    { {- | A"
    , "      -}"
    , "      fooA :: Int"
    , "      {- | B"
    , "      -}"
    , "    , fooB :: Int"
    , "      {- | C"
    , "      -}"
    , "    , fooC :: Int"
    , "      {- | D"
    , "      -}"
    , "    , fooD :: Int"
    , "      {- | E"
    , "      -}"
    , "    , fooE :: Int"
    , "      {- | F"
    , "      -}"
    , "    , fooFooFoo :: Int"
    , "      {- | G"
    , "      -}"
    , "    , fooBarBar :: Int"
    , "    }"
    ]

  | Should not break DataKinds in records
--
  See -haskell/issues/330
case58 :: Assertion
case58 = assertSnippet (step sameIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo a = Foo"
      , "               { foo :: Foo 'True"
      , "               }"
      ]

    expected = input

-- | Don't remove existential quantification
--
-- See -haskell/issues/339
case59 :: Assertion
case59 = assertSnippet (step defaultConfig)
    [ "data Foo = forall (a :: *) . Bar a" ]
    [ "data Foo = forall (a :: *). Bar a" ]

-- | Don't remove existential quantification
--
-- See -haskell/issues/339
case60 :: Assertion
case60 = assertSnippet (step defaultConfig)
    [ "data Foo = forall a . Bar a" ]
    [ "data Foo = forall a. Bar a" ]

  | Formatting duplicates haddock comments # 282
--
-- Regression test for -haskell/issues/282
case61 :: Assertion
case61 = assertSnippet (step sameIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Game = Game { _board    :: Board -- ^ Board state"
      , "                , _time     :: Int   -- ^ Time elapsed"
      , "                , _paused   :: Bool  -- ^ Playing vs. paused"
      , "                , _speed    :: Float -- ^ Speed in [0..1]"
      , "                , _interval :: TVar Int -- ^ Interval kept in TVar"
      , "                }"
      ]

    expected =
      [ "module Herp where"
      , ""
      , "data Game = Game"
      , "              { _board :: Board"
      , "                -- ^ Board state"
      , "              , _time :: Int"
      , "                -- ^ Time elapsed"
      , "              , _paused :: Bool"
      , "                -- ^ Playing vs. paused"
      , "              , _speed :: Float"
      , "                -- ^ Speed in [0..1]"
      , "              , _interval :: TVar Int"
      , "                -- ^ Interval kept in TVar"
      , "              }"
      ]

  | Comment issues with record formatting # 273
--
-- Regression test for -haskell/issues/273
case62 :: Assertion
case62 = assertSnippet (step sameIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo"
    , "   { -- | This is a comment above some line."
    , "    -- It can span multiple lines."
    , "     fooName :: String"
    , "   , fooAge :: Int"
    , "     -- ^ This is a comment below some line."
    , "     -- It can span multiple lines."
    , "   }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo = Foo"
    , "             { -- | This is a comment above some line."
    , "               -- It can span multiple lines."
    , "               fooName :: String"
    , "             , fooAge :: Int"
    , "               -- ^ This is a comment below some line."
    , "               -- It can span multiple lines."
    , "             }"
    ]

case63 :: Assertion
case63 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo :: * -> * where"
      , "  Bar :: () -> Foo ()"
      ]
    expected = input

case64 :: Assertion
case64 = assertSnippet (step indentIndentStyle) input input
  where
    input =
        [ "data Foo"
        , "  = Bar Int"
        , "  -- ^ Following comment"
        , "  | Qux Int"
        , "  -- ^ Second following comment"
        , "  deriving (Show)"
        ]

case65 :: Assertion
case65 = assertSnippet (step indentIndentStyle) input input
  where
    input =
        [ "data Foo"
        , "  = Bar"
        , "  -- ^ Following comment"
        , "  | Qux"
        , "  -- ^ Second following comment"
        , "  deriving (Show)"
        ]

-- | Deriving alignment for enums
--
-- Regression test for -haskell/issues/411
case66 :: Assertion
case66 = assertSnippet (step indentIndentStyle) input input
  where
    input =
      [ "data Foo = A | B | C"
      , "  deriving (Eq, Show)"
      ]

sameSameStyle :: Config
sameSameStyle = Config SameLine SameLine 2 2 False True SameLine False True NoMaxColumns

sameIndentStyle :: Config
sameIndentStyle = Config SameLine (Indent 2) 2 2 False True SameLine False True NoMaxColumns

indentSameStyle :: Config
indentSameStyle = Config (Indent 2) SameLine 2 2 False True SameLine False True NoMaxColumns

indentIndentStyle :: Config
indentIndentStyle = Config (Indent 2) (Indent 2) 2 2 False True SameLine False True NoMaxColumns

indentIndentStyle4 :: Config
indentIndentStyle4 = Config (Indent 4) (Indent 4) 4 4 False True SameLine False True NoMaxColumns

sameSameNoSortStyle :: Config
sameSameNoSortStyle = Config SameLine SameLine 2 2 False True SameLine False False NoMaxColumns

# LANGUAGE OverloadedStrings #

 See -haskell/issues/262
 This test showcases a difficult to solve issue. If the comment is in a
 deriving clause, it's very hard to guess the correct position of the entire
 block. E.g. the deriving clause itself has the wrong position. However, if
 we look at all deriving clauses we know where they start and end.

 This means that we've needed to make the decision to put all inline comments
 before the deriving clause itself

 | Don't remove existential quantification

 See -haskell/issues/339
 | Don't remove existential quantification

 See -haskell/issues/339

 Regression test for -haskell/issues/282

 Regression test for -haskell/issues/273
 | Deriving alignment for enums

 Regression test for -haskell/issues/411
# LANGUAGE OverloadedLists    #
module Language.Haskell.Stylish.Step.Data.Tests
    ( tests
    ) where

import           Language.Haskell.Stylish.Step.Data
import           Language.Haskell.Stylish.Tests.Util (assertSnippet)
import           Test.Framework                      (Test, testGroup)
import           Test.Framework.Providers.HUnit      (testCase)
import           Test.HUnit                          (Assertion)

tests :: Test
tests = testGroup "Language.Haskell.Stylish.Step.Data.Tests"
    [ testCase "case 00" case00
    , testCase "case 01" case01
    , testCase "case 02" case02
    , testCase "case 03" case03
    , testCase "case 04" case04
    , testCase "case 05" case05
    , testCase "case 06" case06
    , testCase "case 07" case07
    , testCase "case 08" case08
    , testCase "case 09" case09
    , testCase "case 10" case10
    , testCase "case 11" case11
    , testCase "case 12" case12
    , testCase "case 13" case13
    , testCase "case 14" case14
    , testCase "case 15" case15
    , testCase "case 16" case16
    , testCase "case 17" case17
    , testCase "case 18" case18
    , testCase "case 19" case19
    , testCase "case 20 (issue #262)" case20
    , testCase "case 21" case21
    , testCase "case 22" case22
    , testCase "case 23" case23
    , testCase "case 24" case24
    , testCase "case 25" case25
    , testCase "case 26" case26
    , testCase "case 27" case27
    , testCase "case 28" case28
    , testCase "case 29" case29
    , testCase "case 30" case30
    , testCase "case 31" case31
    , testCase "case 32" case32
    , testCase "case 33" case33
    , testCase "case 34" case34
    , testCase "case 35" case35
    , testCase "case 36" case36
    , testCase "case 37" case37
    , testCase "case 38" case38
    , testCase "case 39" case39
    , testCase "case 40" case40
    , testCase "case 41" case41
    , testCase "case 42" case42
    , testCase "case 43" case43
    , testCase "case 44" case44
    , testCase "case 45" case45
    , testCase "case 46" case46
    , testCase "case 47" case47
    , testCase "case 48" case48
    , testCase "case 49" case49
    , testCase "case 50" case50
    , testCase "case 51" case51
    , testCase "case 52" case52
    , testCase "case 53" case53
    , testCase "case 54" case54
    , testCase "case 55" case55
    , testCase "case 56" case56
    , testCase "case 57" case57
    , testCase "case 58" case58
    , testCase "case 59" case59
    , testCase "case 60" case60
    , testCase "case 61 (issue #282)" case61
    , testCase "case 62 (issue #273)" case62
    , testCase "case 63 (issue #338)" case63
    , testCase "case 64" case64
    , testCase "case 65" case65
    , testCase "case 66 (issue #411)" case66
    ]

case00 :: Assertion
case00 = assertSnippet (step sameSameStyle) input input
  where
    input =
        [ "module Herp where"
        , ""
        , "data Foo"
        ]

case01 :: Assertion
case01 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo { a :: Int }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "      }"
    ]

case02 :: Assertion
case02 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo { a :: Int, a2 :: String }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "      , a2 :: String"
    , "      }"
    ]

case03 :: Assertion
case03 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo { a :: a, a2 :: String }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "      , a2 :: String"
    , "      }"
    ]

case04 :: Assertion
case04 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo { a :: a, a2 :: String } | Bar { b :: a }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "      , a2 :: String"
    , "      }"
    , "  | Bar"
    , "      { b :: a"
    , "      }"
    ]

case05 :: Assertion
case05 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo {"
    , "  a :: Int"
    , "  , a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "      , a2 :: String"
    , "      }"
    ]

case06 :: Assertion
case06 = assertSnippet (step sameSameStyle) input input
  where
    input =
        [ "module Herp where"
        , ""
        , "data Foo = Foo Int String"
        ]

case07 :: Assertion
case07 = assertSnippet (step sameSameStyle) input input
  where
    input =
        [ "module Herp where"
        , ""
        , "data Phantom a = Phantom"
        ]

case08 :: Assertion
case08 = assertSnippet (step sameSameStyle)
    [ "module Herp where"
    , ""
    , "data Phantom a ="
    , "  Phantom"
    ]
    [ "module Herp where"
    , ""
    , "data Phantom a = Phantom"
    ]

case09 :: Assertion
case09 = assertSnippet (step indentIndentStyle4)
    [ "module Herp where"
    , ""
    , "data Foo a b = Foo { a :: a, a2 :: String } | Bar { b :: a, c:: b }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a b"
    , "    = Foo"
    , "          { a :: a"
    , "          , a2 :: String"
    , "          }"
    , "    | Bar"
    , "          { b :: a"
    , "          , c :: b"
    , "          }"
    ]

case10 :: Assertion
case10 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo = Foo { a :: Int } deriving (Eq, Generic) deriving (Show)"
      ]

    expected =
       [ "module Herp where"
       , ""
       , "data Foo"
       , "  = Foo"
       , "      { a :: Int"
       , "      }"
       , "  deriving (Eq, Generic)"
       , "  deriving (Show)"
       ]

case11 :: Assertion
case11 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "{-# LANGUAGE DerivingStrategies #-}"
      , "module Herp where"
      , ""
      , "data Foo = Foo { a :: Int } deriving stock (Show)"
      ]

    expected =
       [ "{-# LANGUAGE DerivingStrategies #-}"
       , "module Herp where"
       , ""
       , "data Foo"
       , "  = Foo"
       , "      { a :: Int"
       , "      }"
       , "  deriving stock (Show)"
       ]

case12 :: Assertion
case12 = assertSnippet (step indentIndentStyle4) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Point = Point { pointX, pointY :: Double , pointName :: String} deriving (Show)"
      ]

    expected =
       [ "module Herp where"
       , ""
       , "data Point"
       , "    = Point"
       , "          { pointX, pointY :: Double"
       , "          , pointName :: String"
       , "          }"
       , "    deriving (Show)"
       ]

case13 :: Assertion
case13 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "-- this is a comment"
      , "data Foo = Foo { a :: Int }"
      ]
    expected =
      [ "module Herp where"
      , ""
      , "-- this is a comment"
      , "data Foo"
      , "  = Foo"
      , "      { a :: Int"
      , "      }"
      ]

case14 :: Assertion
case14 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "{- this is"
      , "   a comment -}"
      , "data Foo = Foo { a :: Int }"
      ]
    expected =
      [ "module Herp where"
      , ""
      , "{- this is"
      , "   a comment -}"
      , "data Foo"
      , "  = Foo"
      , "      { a :: Int"
      , "      }"
      ]

case15 :: Assertion
case15 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { a :: a, -- comment"
    , "   a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "        -- comment"
    , "      , a2 :: String"
    , "      }"
    ]

case16 :: Assertion
case16 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo {"
    , "   a :: Int -- ^ comment"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo"
    , "  = Foo"
    , "      { a :: Int"
    , "        -- ^ comment"
    , "      }"
    ]

case17 :: Assertion
case17 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { a :: a,"
    , "-- comment"
    , "   a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "        -- comment"
    , "      , a2 :: String"
    , "      }"
    ]

case18 :: Assertion
case18 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { a :: a,"
    , "-- ^ comment"
    , "   a2 :: String"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { a :: a"
    , "        -- ^ comment"
    , "      , a2 :: String"
    , "      }"
    ]

case19 :: Assertion
case19 = assertSnippet (step indentIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo a = Foo"
    , "  { firstName, lastName :: String,"
    , "-- ^ names"
    , "   age :: Int"
    , "  }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo a"
    , "  = Foo"
    , "      { firstName, lastName :: String"
    , "        -- ^ names"
    , "      , age :: Int"
    , "      }"
    ]

  | Should not break Enums ( data without records ) formatting
case20 :: Assertion
case20 = assertSnippet (step indentIndentStyle) input input
  where
    input =
       [ "module Herp where"
       , ""
       , "data Tag = Title | Text"
       , "  deriving (Eq, Show)"
       ]

case21 :: Assertion
case21 = assertSnippet (step sameSameStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a = Foo { a :: Int"
    , "                 , a2 :: String"
    , "                   -- ^ some haddock"
    , "                 }"
    , "           | Bar { b :: a"
    , "                 }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case22 :: Assertion
case22 = assertSnippet (step sameIndentStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a = Foo"
    , "               { a :: Int"
    , "               , a2 :: String"
    , "                 -- ^ some haddock"
    , "               }"
    , "           | Bar"
    , "               { b :: a"
    , "               }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case23 :: Assertion
case23 = assertSnippet (step indentSameStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a"
    , "  = Foo { a :: Int"
    , "        , a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a"
    , "        }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case24 :: Assertion
case24 = assertSnippet (step indentIndentStyle)
    [ "data Foo a"
    , "  = Foo { a :: Int,"
    , "          a2 :: String"
    , "          -- ^ some haddock"
    , "        }"
    , "  | Bar { b :: a }  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]
    [ "data Foo a"
    , "  = Foo"
    , "      { a :: Int"
    , "      , a2 :: String"
    , "        -- ^ some haddock"
    , "      }"
    , "  | Bar"
    , "      { b :: a"
    , "      }"
    , "  deriving (Eq, Show)"
    , "  deriving (ToJSON)"
    ]

case25 :: Assertion
case25 = assertSnippet (step indentIndentStyle { cBreakSingleConstructors = False }) input expected
  where
    input =
       [ "data Foo a"
       , "  = Foo { a :: Int,"
       , "          a2 :: String"
       , "          -- ^ some haddock"
       , "        }"
       , "  deriving (Eq, Show)"
       , "  deriving (ToJSON)"
       ]

    expected =
       [ "data Foo a = Foo"
       , "  { a :: Int"
       , "  , a2 :: String"
       , "    -- ^ some haddock"
       , "  }"
       , "  deriving (Eq, Show)"
       , "  deriving (ToJSON)"
       ]

case26 :: Assertion
case26 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo = Foo { a :: Int } deriving (FromJSON) via Bla Foo"
      ]

    expected =
       [ "module Herp where"
       , ""
       , "data Foo"
       , "  = Foo"
       , "      { a :: Int"
       , "      }"
       , "  deriving (FromJSON) via Bla Foo"
       ]

case27 :: Assertion
case27 = assertSnippet (step sameIndentStyle { cBreakEnums = True }) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo = Foo | Bar | Baz deriving (Eq, Show)"
      ]

    expected =
      [ "module Herp where"
      , ""
      , "data Foo"
      , "  = Foo"
      , "  | Bar"
      , "  | Baz"
      , "  deriving (Eq, Show)"
      ]

case28 :: Assertion
case28 = assertSnippet (step sameIndentStyle { cBreakEnums = True }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype BankCode = BankCode {"
      , "    unBankCode :: Text"
      , "  }"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype CheckDigit = CheckDigit { unCheckDigit :: Text }"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype WrappedInt = WrappedInt Int"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "data MandateStatus"
      , "  = Approved"
      , "  | Failed"
      , "  | UserCanceled"
      , "  | Inactive"
      , "  deriving stock (Generic, Show, Eq, Enum, Bounded)"
      , "  deriving (ToJSON, FromJSON) via SnakeCaseCapsEnumEncoding MandateStatus"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype BankCode = BankCode { unBankCode :: Text }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype CheckDigit = CheckDigit { unCheckDigit :: Text }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "newtype WrappedInt = WrappedInt Int"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving anyclass (Newtype)"
      , ""
      , "data MandateStatus"
      , "  = Approved"
      , "  | Failed"
      , "  | UserCanceled"
      , "  | Inactive"
      , "  deriving stock (Bounded, Enum, Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON) via SnakeCaseCapsEnumEncoding MandateStatus"
      ]

case29 :: Assertion
case29 = assertSnippet (step sameIndentStyle) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "data NonEmpty a"
      , "  = a :| [a]"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "data NonEmpty a = a :| [a]"
      ]

case30 :: Assertion
case30 = assertSnippet (step sameIndentStyle { cBreakEnums = True }) input expected
  where
    expected = input
    input =
      [ "data ReasonCode"
      , "  = MissingTenantId"
      , "  -- Transaction errors:"
      , "  | TransactionDoesNotExist"
      , "  | TransactionAlreadyExists"
      , "  -- Engine errors:"
      , "  | EnginePersistenceError"
      , "  | EngineValidationError"
      , "  -- | Transaction was created in Info mode"
      , "  | RegisteredByNetworkEngine"
      , "  -- | Transaction was created in Routing mode"
      , "  | SentToNetworkEngine"
      , "  -- Network connection reasons:"
      , "  | SentToNetworkConnection"
      , "  | ReceivedByNetworkConnection"
      , "  | ValidatedByNetworkConnection"
      ]


case31 :: Assertion
case31 = assertSnippet (step indentIndentStyle { cBreakEnums = True }) input expected
  where
    expected = input
    input =
      [ "data ConfiguredLogger"
      , "  -- | Logs to file"
      , "  = LogTo FilePath"
      , "  -- | Logs to stdout"
      , "  | LogToConsole"
      , "  -- | No logging, discards all messages"
      , "  | NoLogging"
      , "  deriving stock (Generic, Show)"
      ]

case32 :: Assertion
case32 = assertSnippet (step indentIndentStyle { cBreakEnums = True }) input expected
  where
    expected = input
    input =
      [ "data RejectionReason"
      , "  -- InvalidState"
      , "  = CancellationFailed"
      , "  | TotalAmountConfirmationInvalid"
      , "  -- InvalidApiUsage"
      , "  | AccessTokenNotActive"
      , "  | VersionNotFound"
      , "  -- ValidationFailed"
      , "  | BankAccountExists"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON) via SnakeCaseLowercaseEnumEncoding RejectionReason"
      ]

case33 :: Assertion
case33 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a = NonEmpty { unNonEmpty :: a }"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a"
      , "  = NonEmpty { unNonEmpty :: a }"
      ]

case34 :: Assertion
case34 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a = NonEmpty { unNonEmpty :: a }"
      , "     deriving (ToJSON, FromJSON) via Something Magic (NonEmpty a)"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype NonEmpty a"
      , "  = NonEmpty { unNonEmpty :: a }"
      , "  deriving (FromJSON, ToJSON)"
      , "    via Something Magic (NonEmpty a)"
      ]

case35 :: Assertion
case35 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: MonetaryAmount"
      , "      -> TransactionId"
      , "      -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: MonetaryAmount -> TransactionId -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

case36 :: Assertion
case36 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: (a -> b)"
      , "      -> TransactionId"
      , "      -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "data Foo = Foo"
      , "  { _transfer :: (a -> b) -> TransactionId -> m (Either CreditTransferError TransactionId)"
      , "  }"
      ]

case37 :: Assertion
case37 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "module Some.Types where"
      , ""
      , "newtype UndoFlowData"
      , "  = UndoFlowData { flowDataDetails :: FlowDataDetails }"
      , "  deriving stock (Generic, Eq, Show)"
      , "  deriving (ToJSON, FromJSON)"
      , "    via AddConstTextFields '[\"type0\" := \"undo\","
      , "                             \"type1\" := \"undo\","
      , "                     \"reversal_indicator\" := \"Undo\"] FlowDataDetails"
      ]

    expected =
      [ "module Some.Types where"
      , ""
      , "newtype UndoFlowData"
      , "  = UndoFlowData { flowDataDetails :: FlowDataDetails }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via AddConstTextFields '[\"type0\" := \"undo\", \"type1\" := \"undo\", \"reversal_indicator\" := \"Undo\"] FlowDataDetails"
      ]

case38 :: Assertion
case38 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "data Flat = Flat"
      , "  { foo :: Int"
      , "  , bar :: Text"
      , "  , baz :: Double"
      , "  , qux :: Bool"
      , "  }"
      , "  deriving stock (Generic, Show, Eq)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via GenericEncoded"
      , "      '[ FieldLabelModifier :="
      , "        '[ \"foo\" ==> \"nestFoo#foo\""
      , "         , \"bar\" ==> \"nestBar#bar\""
      , "         , \"baz\" ==> \"nestFoo#baz\""
      , "         ]"
      , "       ]"
      , "      Flat"
      ]

    expected =
      [ "data Flat"
      , "  = Flat"
      , "      { foo :: Int"
      , "      , bar :: Text"
      , "      , baz :: Double"
      , "      , qux :: Bool"
      , "      }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via GenericEncoded '[FieldLabelModifier := '[\"foo\" ==> \"nestFoo#foo\", \"bar\" ==> \"nestBar#bar\", \"baz\" ==> \"nestFoo#baz\"]] Flat"
      ]

case39 :: Assertion
case39 = assertSnippet (step indentIndentStyle { cVia = Indent 2 }) input expected
  where
    input =
      [ "data CreditTransfer = CreditTransfer"
      , "  { nestedCreditorInfo :: CreditorInfo"
      , "  }"
      , "  deriving stock (Show, Eq, Generic)"
      , "  deriving (ToJSON, FromJSON) via"
      , "    ( UntaggedEncoded NordeaCreditTransfer"
      , "    & AddConstTextFields"
      , "      '[ \"request_type\" ':= \"credit_transfer\""
      , "       , \"provider\" ':= \"nordea\""
      , "       ]"
      , "    & FlattenFields '[\"nested_creditor_info\"]"
      , "    & RenameKeys"
      , "        '[ \"nested_creditor_info.creditor_agent_bic\" ==> \"creditor_agent_bic\""
      , "         , \"nested_creditor_info.creditor_iban\" ==> \"creditor_iban\""
      , "         , \"nested_creditor_info.creditor_name\" ==> \"creditor_name\""
      , "         , \"nested_creditor_info.creditor_account\" ==> \"creditor_account\""
      , "         ]"
      , "    )"
      ]

    expected =
      [ "data CreditTransfer"
      , "  = CreditTransfer"
      , "      { nestedCreditorInfo :: CreditorInfo"
      , "      }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via (UntaggedEncoded NordeaCreditTransfer & AddConstTextFields '[\"request_type\" ':= \"credit_transfer\", \"provider\" ':= \"nordea\"] & FlattenFields '[\"nested_creditor_info\"] & RenameKeys '[\"nested_creditor_info.creditor_agent_bic\" ==> \"creditor_agent_bic\", \"nested_creditor_info.creditor_iban\" ==> \"creditor_iban\", \"nested_creditor_info.creditor_name\" ==> \"creditor_name\", \"nested_creditor_info.creditor_account\" ==> \"creditor_account\"])"
      ]

case40 :: Assertion
case40 = assertSnippet (step indentIndentStyle { cBreakSingleConstructors = False }) input expected
  where
    input =
      [ "module X where"
      , ""
      , "data a :==> b    ="
      , "                  Arr a b"
      ]

    expected =
      [ "module X where"
      , ""
      , "data a :==> b = Arr a b"
      ]

case41 :: Assertion
case41 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected

    expected =
      [ "module X where"
      , ""
      , "data Callback"
      , "  -- | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor"
      , "  --   incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis"
      , "  --   nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
      , "  --   Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore"
      , "  --   eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,"
      , "  --   sunt in culpa qui officia deserunt mollit anim id est laborum."
      , "  = KafkaTopic"
      , "      { callbackTopic :: CallbackTopic"
      , "        -- ^ Name of topic to send updates to"
      , "      , callbackFormat :: CallbackFormat"
      , "        -- ^ The format used to send these updates"
      , "      }"
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON) via IdiomaticWithDescription CallbackDesc Callback"
      , "  deriving (HasGen) via Generically Callback"
      , "  deriving (FromField) via JsonField Callback"
      ]

case42 :: Assertion
case42 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected

    expected =
      [ "module X where"
      , ""
      , "data SignupError"
      , "  = IdempotencyConflict"
      , "  | ValidationError Text -- TODO: might be a sumtype of possible error codes"
      , "  deriving stock (Eq, Generic, Show)"
      ]

case43 :: Assertion
case43 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False }) input expected
  where
    input = expected

    expected =
      [ "module X where"
      , ""
      , "data CallbackResult"
      , "  -- | Callback successfully sent"
      , "  = Success"
      , "  -- | Kafka error received"
      , "  | KafkaIssue KafkaError"
      , "  deriving (Eq, Show)"
      ]

case44 :: Assertion
case44 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False, cVia = Indent 2 }) input expected
  where
    input =
      [ "module X where"
      , ""
      , " data CreditTransfer = CreditTransfer"
      , "   { amount :: Amount -- ^ 1 <= amount <= 999_999_999_999"
      , "   , date :: Day"
      , "   , accountNumber :: Account"
      , "   }"
      , "   deriving stock (Eq, Generic, Show)"
      , "   deriving (FromJSON, ToJSON) via"
      , "     AddConstTextFields"
      , "       '[\"notification_type\" ':= \"credit_transaction\""
      , "         -- Note that the bcio name has \"transaction\""
      , "         -- rather than \"transfer\""
      , "        ]"
      , "        (UntaggedEncoded CreditTransfer)"
      ]
    expected =
      [ "module X where"
      , ""
      , "data CreditTransfer = CreditTransfer"
      , "  { amount :: Amount"
      , "    -- ^ 1 <= amount <= 999_999_999_999"
      , "  , date :: Day"
      , "  , accountNumber :: Account"
      , "  }"
      , "  -- Note that the bcio name has \"transaction\""
      , "  -- rather than \"transfer\""
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via AddConstTextFields '[\"notification_type\" ':= \"credit_transaction\"] (UntaggedEncoded CreditTransfer)"
      ]

case45 :: Assertion
case45 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False, cVia = Indent 2 }) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "data CreditTransfer = CreditTransfer"
      , "  { amount :: Amount"
      , "    -- ^ 1 <= amount <= 999_999_999_999"
      , "  , date :: Day"
      , "  , accountNumber :: Account"
      , "  }"
      , "  -- Note that the bcio name has \"transaction\""
      , "  -- rather than \"transfer\""
      , "  deriving stock (Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via AddConstTextFields '[\"notification_type\" ':= \"credit_transaction\"] (UntaggedEncoded CreditTransfer)"
      ]

case46 :: Assertion
case46 = assertSnippet (step indentIndentStyle { cBreakEnums = True, cBreakSingleConstructors = False, cVia = Indent 2 }) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A format detailing which encoding to use for the settlement events"
      , "data CallbackFormat"
      , "  -- | The Avro schema is to be used"
      , "  = AvroEngineEvent"
      , "  deriving (Bounded, Enum, Eq, Generic, Show)"
      , "  deriving (FromJSON, ToJSON)"
      , "    via TypeTaggedWithDescription FormatDesc CallbackFormat"
      , "  deriving (HasGen)"
      , "    via EnumBounded CallbackFormat"
      ]

case47 :: Assertion
case47 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: (a, a) -> T [a]"
      ]

case48 :: Assertion
case48 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. (Eq a, Bounded a) => (a, a) -> T [a]"
      ]

case49 :: Assertion
case49 = assertSnippet (step indentIndentStyle) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. (Eq a) => (a, a) -> T [a]"
      ]

case50 :: Assertion
case50 = assertSnippet (step indentIndentStyle { cCurriedContext = True }) input expected
  where
    input = expected
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. Eq a => (a, a) -> T [a]"
      ]

case51 :: Assertion
case51 = assertSnippet (step indentIndentStyle { cCurriedContext = True }) input expected
  where
    input =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. (Eq a) => (a, a) -> T [a]"
      ]
    expected =
      [ "module X where"
      , ""
      , "-- | A GADT example"
      , "data T a where"
      , "  D1 :: Int -> T String"
      , "  D2 :: T Bool"
      , "  D3 :: forall a. Eq a => (a, a) -> T [a]"
      ]

case52 :: Assertion
case52 = assertSnippet (step indentIndentStyle { cBreakSingleConstructors = False, cCurriedContext = True }) input expected
  where
    input =
      [ "module X where"
      , ""
      , "data Foo = Foo"
      , "  { foo :: forall a b. (Eq a, Bounded b) => a -> b -> [(a, b)]"
      , "  }"
      ]
    expected =
      [ "module X where"
      , ""
      , "data Foo = Foo"
      , "  { foo :: forall a b. Eq a => Bounded b => a -> b -> [(a, b)]"
      , "  }"
      ]

case53 :: Assertion
case53 = assertSnippet (step indentIndentStyle { cMaxColumns = MaxColumns 80 }) input expected
  where
    input =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype (Functor, Applicative, Monad, MonadError, MonadCatch, Foldable, Monoid)"
      ]
    expected =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype"
      , "    ( Applicative"
      , "    , Foldable"
      , "    , Functor"
      , "    , Monad"
      , "    , MonadCatch"
      , "    , MonadError"
      , "    , Monoid"
      , "    )"
      ]

case54 :: Assertion
case54 = assertSnippet (step indentIndentStyle { cMaxColumns = MaxColumns 80 }) input expected
  where
    input =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype (Functor, Applicative, Monad)"
      ]
    expected =
      [ "newtype Foo m a"
      , "  = Foo (m a)"
      , "  deriving newtype (Applicative, Functor, Monad)"
      ]

case55 :: Assertion
case55 = assertSnippet (step sameSameNoSortStyle) input expected
  where
    input =
       [ "data Foo = Foo"
       , "  deriving (Z, Y, X, Bar, Abcd)"
       ]

    expected = input

case56 :: Assertion
case56 = assertSnippet (step defaultConfig)
    [ "data Foo = Foo"
    , "  { -- | Comment"
    , "    bar :: Int"
    , "  , baz :: Int"
    , "  }"
    ]
    [ "data Foo = Foo"
    , "    { -- | Comment"
    , "      bar :: Int"
    , "    , baz :: Int"
    , "    }"
    ]

case57 :: Assertion
case57 = assertSnippet (step defaultConfig)
    [ "data Foo = Foo"
    , "    { {- | A"
    , "      -}"
    , "      fooA :: Int"
    , ""
    , "      {- | B"
    , "      -}"
    , "    , fooB :: Int"
    , ""
    , "      {- | C"
    , "      -}"
    , "    , fooC :: Int"
    , ""
    , "      {- | D"
    , "      -}"
    , "    , fooD :: Int"
    , ""
    , "      {- | E"
    , "      -}"
    , "    , fooE :: Int"
    , ""
    , "      {- | F"
    , "      -}"
    , "    , fooFooFoo :: Int"
    , ""
    , "      {- | G"
    , "      -}"
    , "    , fooBarBar :: Int"
    , "    }"
    ]
    [ "data Foo = Foo"
    , "    { {- | A"
    , "      -}"
    , "      fooA :: Int"
    , "      {- | B"
    , "      -}"
    , "    , fooB :: Int"
    , "      {- | C"
    , "      -}"
    , "    , fooC :: Int"
    , "      {- | D"
    , "      -}"
    , "    , fooD :: Int"
    , "      {- | E"
    , "      -}"
    , "    , fooE :: Int"
    , "      {- | F"
    , "      -}"
    , "    , fooFooFoo :: Int"
    , "      {- | G"
    , "      -}"
    , "    , fooBarBar :: Int"
    , "    }"
    ]

  | Should not break DataKinds in records
  See -haskell/issues/330
case58 :: Assertion
case58 = assertSnippet (step sameIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo a = Foo"
      , "               { foo :: Foo 'True"
      , "               }"
      ]

    expected = input

case59 :: Assertion
case59 = assertSnippet (step defaultConfig)
    [ "data Foo = forall (a :: *) . Bar a" ]
    [ "data Foo = forall (a :: *). Bar a" ]

case60 :: Assertion
case60 = assertSnippet (step defaultConfig)
    [ "data Foo = forall a . Bar a" ]
    [ "data Foo = forall a. Bar a" ]

  | Formatting duplicates haddock comments # 282
case61 :: Assertion
case61 = assertSnippet (step sameIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Game = Game { _board    :: Board -- ^ Board state"
      , "                , _time     :: Int   -- ^ Time elapsed"
      , "                , _paused   :: Bool  -- ^ Playing vs. paused"
      , "                , _speed    :: Float -- ^ Speed in [0..1]"
      , "                , _interval :: TVar Int -- ^ Interval kept in TVar"
      , "                }"
      ]

    expected =
      [ "module Herp where"
      , ""
      , "data Game = Game"
      , "              { _board :: Board"
      , "                -- ^ Board state"
      , "              , _time :: Int"
      , "                -- ^ Time elapsed"
      , "              , _paused :: Bool"
      , "                -- ^ Playing vs. paused"
      , "              , _speed :: Float"
      , "                -- ^ Speed in [0..1]"
      , "              , _interval :: TVar Int"
      , "                -- ^ Interval kept in TVar"
      , "              }"
      ]

  | Comment issues with record formatting # 273
case62 :: Assertion
case62 = assertSnippet (step sameIndentStyle)
    [ "module Herp where"
    , ""
    , "data Foo = Foo"
    , "   { -- | This is a comment above some line."
    , "    -- It can span multiple lines."
    , "     fooName :: String"
    , "   , fooAge :: Int"
    , "     -- ^ This is a comment below some line."
    , "     -- It can span multiple lines."
    , "   }"
    ]
    [ "module Herp where"
    , ""
    , "data Foo = Foo"
    , "             { -- | This is a comment above some line."
    , "               -- It can span multiple lines."
    , "               fooName :: String"
    , "             , fooAge :: Int"
    , "               -- ^ This is a comment below some line."
    , "               -- It can span multiple lines."
    , "             }"
    ]

case63 :: Assertion
case63 = assertSnippet (step indentIndentStyle) input expected
  where
    input =
      [ "module Herp where"
      , ""
      , "data Foo :: * -> * where"
      , "  Bar :: () -> Foo ()"
      ]
    expected = input

case64 :: Assertion
case64 = assertSnippet (step indentIndentStyle) input input
  where
    input =
        [ "data Foo"
        , "  = Bar Int"
        , "  -- ^ Following comment"
        , "  | Qux Int"
        , "  -- ^ Second following comment"
        , "  deriving (Show)"
        ]

case65 :: Assertion
case65 = assertSnippet (step indentIndentStyle) input input
  where
    input =
        [ "data Foo"
        , "  = Bar"
        , "  -- ^ Following comment"
        , "  | Qux"
        , "  -- ^ Second following comment"
        , "  deriving (Show)"
        ]

case66 :: Assertion
case66 = assertSnippet (step indentIndentStyle) input input
  where
    input =
      [ "data Foo = A | B | C"
      , "  deriving (Eq, Show)"
      ]

sameSameStyle :: Config
sameSameStyle = Config SameLine SameLine 2 2 False True SameLine False True NoMaxColumns

sameIndentStyle :: Config
sameIndentStyle = Config SameLine (Indent 2) 2 2 False True SameLine False True NoMaxColumns

indentSameStyle :: Config
indentSameStyle = Config (Indent 2) SameLine 2 2 False True SameLine False True NoMaxColumns

indentIndentStyle :: Config
indentIndentStyle = Config (Indent 2) (Indent 2) 2 2 False True SameLine False True NoMaxColumns

indentIndentStyle4 :: Config
indentIndentStyle4 = Config (Indent 4) (Indent 4) 4 4 False True SameLine False True NoMaxColumns

sameSameNoSortStyle :: Config
sameSameNoSortStyle = Config SameLine SameLine 2 2 False True SameLine False False NoMaxColumns
