module System.HapistranoPropsSpec
  ( spec
  ) where

import           Data.Char                           (isSpace)
import           System.Hapistrano.Commands.Internal (mkGenericCommand,
                                                      quoteCmd, trim,
                                                      unGenericCommand)
import           Test.Hspec                          hiding (shouldBe,
                                                      shouldReturn)
import           Test.QuickCheck

spec :: Spec
spec =
  describe "QuickCheck" $
  context "Properties" $ do
    it "property of quote command" $ property propQuote'
    it "property of trimming a command" $
      property $ forAll trimGenerator propTrim'
    it "property of mkGenericCommand and unGenericCommand" $
      property $ forAll genericCmdGenerator propGenericCmd'

-- Is quoted determine
isQuoted :: String -> Bool
isQuoted str = head str == '"' && last str == '"'

-- | Quote function property
propQuote :: String -> Bool
propQuote str =
  if any isSpace str
    then isQuoted $ quoteCmd str
    else quoteCmd str == str

propQuote' :: String -> Property
propQuote' str =
  classify (any isSpace str) "has at least a space" $ propQuote str

-- | Is trimmed
isTrimmed' :: String -> Bool
isTrimmed' [] = True
isTrimmed' [x] = not $ isSpace x
isTrimmed' str =
  let a = not . isSpace $ head str
      b = not . isSpace $ last str
   in a && b

-- | Prop trimm
propTrim :: String -> Bool
propTrim = isTrimmed' . trim

propTrim' :: String -> Property
propTrim' str =
  classify (not $ isTrimmed' str) "non trimmed strings" $ propTrim str

-- | Check that the string is perfect command String
isCmdString :: String -> Bool
isCmdString str = all ($str) [not . null, notElem '#', notElem '\n', isTrimmed']

-- | Prop Generic Command
-- If the string does not contain # or \n, is trimmed and non null, the command should be created
propGenericCmd :: String -> Bool
propGenericCmd str =
  if isCmdString str
    then maybe False ((== str) . unGenericCommand) (mkGenericCommand str)
    else maybe True ((/= str) . unGenericCommand) (mkGenericCommand str) -- Either the command cannot be created or the command str is different to the original

propGenericCmd' :: String -> Property
propGenericCmd' str =
  classify (isCmdString str) "perfect command string" propGenericCmd

-- | Trim String Generator
trimGenerator :: Gen String
trimGenerator =
  let strGen = listOf arbitraryUnicodeChar
   in frequency
        [ (1, suchThat strGen isTrimmed')
        , (1, suchThat strGen (not . isTrimmed'))
        ]

-- | Generic Command generator
genericCmdGenerator :: Gen String
genericCmdGenerator =
  let strGen = listOf $ elements $ ['A'..'Z'] ++ ['a'..'z'] ++ [' ', '#', '*', '/', '.']
   in frequency
        [(1, suchThat strGen isCmdString), (1, suchThat strGen (elem '#'))]