content: {-# LANGUAGE ScopedTypeVariables #-}
  
  module FeatureSpec where
  
  import qualified Hi.Cli              as Cli
  import           Hi.Directory        (inDirectory)
  
  import           Control.Applicative
  import           Control.Exception   (catch, throwIO)
  import           Data.Time.Calendar  (toGregorian)
  import           Data.Time.Clock     (getCurrentTime, utctDay)
  import           Data.Version        (showVersion)
  import           SpecHelper
  import           System.Directory    (doesDirectoryExist, doesFileExist,
                                        getCurrentDirectory)
  import           System.Exit         (ExitCode (..))
  import           System.FilePath     (joinPath, (</>))
  import           System.IO           (stdout)
  import           System.IO.Silently  (capture, hSilence)
  import           System.Process      (readProcess, system)
  import           Test.Hspec
  
  import           Paths_hi            (version)
  
  main :: IO ()
  main = hspec spec
  
  spec :: Spec
  spec = do
      describe "with command line options" $ do
        let cmd = runWithCommandLineOptions [ "-p", packageName , "-m", moduleName ]
        around_ cmd features
  
      describe "with custom git config" $ do
        let cmd = runWithLocalGitConfig [ "-p", packageName , "-m", moduleName ]
        around_ cmd features
  
      describe "with config file" $ do
        let cmd = runWithConfigurationFile [ "-p", packageName , "-m", moduleName ]
        around_ cmd features
  
      describe "Only package name was given" $ do
        let cmd = runWithCommandLineOptions ["-p", "something-weird"]
  
        around_ cmd $ do
          it "should use Capitalized package name as module name" $ do
            doesDirectoryExist "something-weird/src/Something/Weird" `shouldReturn` True
  
      describe "Package name was given as an argument" $ do
        let cmd = runWithCommandLineOptions ["something-weird"]
  
        around_ cmd $ do
          it "should use Capitalized package name as module name" $ do
            doesDirectoryExist "something-weird/src/Something/Weird" `shouldReturn` True
  
      describe "Directory name was given" $ do
        let cmd = runWithCommandLineOptions ["abc", "-d", "efg"]
  
        around_ cmd $ do
          it "should create file in given directory" $ do
            doesDirectoryExist "efg/src/Abc" `shouldReturn` True
  
     -- https://github.com/fujimura/hi/issues/48
      describe "with --module-name, not --moduleName" $ do
        let cmd = runWithConfigurationFile [ "-p", packageName , "--module-name", moduleName ]
        around_ cmd features
  
      describe "with --initialize-git-repository" $ do
        let cmd = runWithCommandLineOptions [ "--initialize-git-repository"
                                            , "-p"
                                            , packageName
                                            , "-m"
                                            , moduleName ]
        around_ cmd $ do
          it "should initialize it as git repository and make first commit" $ do
            inDirectory "./testapp" $ do
              readProcess "git" ["log", "-1", "--pretty=%s"] [] `shouldReturn` "Initial commit\n"
  
      describe "with --after-command='touch a" $ do
        let cmd = runWithCommandLineOptions [ "--after-command"
                                            , "touch a"
                                            , "-p"
                                            , packageName
                                            ]
        around_ cmd $ do
          it "should initialize it as git repository and make file named a" $ do
            doesFileExist "testapp/a" `shouldReturn` True
  
      describe "-v" $ do
        it "should show version number" $ do
          let handle ExitSuccess   = return ()
              handle e             = throwIO e
  
          (res,_) <- capture $ Cli.run ["-v"] `catch` handle
          res `shouldBe` (showVersion version) ++ "\n"
  
  packageName, moduleName, author, email :: String
  packageName = "testapp"
  moduleName  = "System.Awesome.Library"
  author      = "Fujimura Daisuke"
  email       = "me@fujimuradaisuke.com"
  
  features :: Spec
  features = do
  
    describe "LICENSE" $ do
      it "should include author" $  do
        compiled <- readFile "testapp/LICENSE"
        compiled `shouldContain` "Fujimura Daisuke"
  
      it "should include year" $  do
        (year,_,_) <- (toGregorian . utctDay) <$> getCurrentTime
        compiled   <- readFile "testapp/LICENSE"
        compiled `shouldContain` show year
  
    describe "README.md" $ do
      it "should include name" $  do
        compiled <- readFile "testapp/README.md"
        compiled `shouldContain` "testapp"
  
    describe "module-name.cabal" $ do
      it "should include name" $  do
        compiled <- readFile "testapp/testapp.cabal"
        compiled `shouldContain` "testapp"
  
      it "should include author" $  do
        compiled <- readFile "testapp/testapp.cabal"
        compiled `shouldContain` "Fujimura Daisuke"
  
      it "should include email" $  do
        compiled <- readFile "testapp/testapp.cabal"
        compiled `shouldContain` "me@fujimuradaisuke.com"
  
      it "should include exposed-modules" $  do
        compiled <- readFile "testapp/testapp.cabal"
        compiled `shouldContain` "Exposed-Modules:      System.Awesome.Library"
  
      it "should include other-modules" $  do
        compiled <- readFile "testapp/testapp.cabal"
        compiled `shouldContain` "Other-Modules:        System.Awesome.Library.Internal"
  
    describe "directory" $ do
      it "should be made according to given module name" $  do
        doesDirectoryExist "testapp/src/System/Awesome/Library" `shouldReturn` True
  
    describe "Main module" $ do
      it "should be made" $  do
        doesFileExist "testapp/src/System/Awesome/Library.hs" `shouldReturn` True
  
      it "should include proper module name" $  do
        compiled <- readFile "testapp/src/System/Awesome/Library.hs"
        compiled `shouldContain` "module System.Awesome.Library"
  
    describe "Internal module" $ do
      it "should be made" $  do
        doesFileExist "testapp/src/System/Awesome/Library/Internal.hs" `shouldReturn` True
  
      it "should include proper module name" $  do
        compiled <- readFile "testapp/src/System/Awesome/Library/Internal.hs"
        compiled `shouldContain` "module System.Awesome.Library.Internal"
  
    describe "Spec.hs" $ do
      it "should be made" $  do
        doesFileExist "testapp/test/Spec.hs" `shouldReturn` True
  
      it "should include proper content" $  do
        compiled <- readFile "testapp/test/Spec.hs"
        compiled `shouldContain` "{-# OPTIONS_GHC -F -pgmF hspec-discover #-}"
  
    describe "Main spec" $ do
      it "should be made" $  do
        doesFileExist "testapp/test/System/Awesome/LibrarySpec.hs" `shouldReturn` True
  
      it "should include proper content" $  do
        compiled <- readFile "testapp/test/System/Awesome/LibrarySpec.hs"
        compiled `shouldContain` "module System.Awesome.LibrarySpec (main, spec) where"
  
    describe ".gitignore" $ do
      it "should be made" $  do
        doesFileExist "testapp/.gitignore" `shouldReturn` True
  
  runWithCommandLineOptions :: [String] -> IO () -> IO ()
  runWithCommandLineOptions opts action = do
      root <- getCurrentDirectory
      inTestDirectory $ hSilence [stdout] $ do
        Cli.run $ opts ++ [ "-a", quote author
                          , "-e", quote email
                          , "-r", (root </> "test" </> "template")
                          ]
        action
  
  runWithLocalGitConfig :: [String] -> IO () -> IO ()
  runWithLocalGitConfig opts action = do
      root <- getCurrentDirectory
      inTestDirectory $ hSilence [stdout] $ do
          _ <- system $ "git init"
          _ <- system $ "git config user.name" ++ " " ++ quote author
          _ <- system $ "git config user.email" ++ " " ++ quote email
          Cli.run $ opts ++ [ "-r", (root </> "test" </> "template") ]
          action
  
  runWithConfigurationFile :: [String] -> IO () -> IO ()
  runWithConfigurationFile opts action = do
      root <- getCurrentDirectory
      inTestDirectory $ hSilence [stdout] $ do
          writeFile ".hirc" $ concatLines
              [ "author: " ++ author
              , "email: " ++ email
              ]
          pwd' <- getCurrentDirectory
          withEnv "HOME" pwd' $ do
            Cli.run $ opts ++ [ "--configuration-file", (joinPath [pwd', ".hirc"])
                              , "-r", (root </> "test" </> "template")
                              ]
            action
  
  {-# ANN module "HLint: Redundant do" #-}
  