{-# LANGUAGE OverloadedStrings #-}
module HpackSpec (spec) where

import           Helper

import           Prelude hiding (readFile)
import qualified Prelude as Prelude
import           System.Exit (die)

import           Control.DeepSeq

import           Hpack.Config
import           Hpack.CabalFile
import           Hpack.Error (formatHpackError)
import           Hpack hiding (hpack)

readFile :: FilePath -> IO String
readFile name = Prelude.readFile name >>= (return $!!)

spec :: Spec
spec = do
  describe "header" $ do
    it "generates header" $ do
      header "foo.yaml" Nothing Nothing `shouldBe` [
          "-- This file has been generated from foo.yaml by hpack."
        , "--"
        , "-- see: https://github.com/sol/hpack"
        , ""
        ]

    context "with hpack version" $ do
      it "includes hpack version" $ do
        header "foo.yaml" (Just $ makeVersion [0,34,0]) Nothing `shouldBe` [
            "-- This file has been generated from foo.yaml by hpack version 0.34.0."
          , "--"
          , "-- see: https://github.com/sol/hpack"
          , ""
          ]

    context "with hash" $ do
      it "includes hash" $ do
        header "foo.yaml" Nothing (Just "some-hash") `shouldBe` [
            "-- This file has been generated from foo.yaml by hpack."
          , "--"
          , "-- see: https://github.com/sol/hpack"
          , "--"
          , "-- hash: some-hash"
          , ""
          ]

  describe "renderCabalFile" $ do
    it "is inverse to readCabalFile" $ do
      expected <- lines <$> readFile "resources/test/hpack.cabal"
      Just c <- readCabalFile "resources/test/hpack.cabal"
      renderCabalFile "package.yaml" c `shouldBe` expected

  describe "hpackResult" $ around_ inTempDirectory $ before_ (writeFile packageConfig "name: foo") $ do
    let
      file = "foo.cabal"

      hpackWithVersion v = hpackResultWithVersion (makeVersion v) defaultOptions >>= either (die . formatHpackError "hpack") return
      hpackWithStrategy strategy = hpackResult defaultOptions { optionsGenerateHashStrategy = strategy }
      hpackForce = hpackResult defaultOptions {optionsForce = Force}

      generated = Result [] file Generated
      modifiedManually = Result [] file ExistingCabalFileWasModifiedManually
      outputUnchanged = Result [] file OutputUnchanged
      alreadyGeneratedByNewerHpack = Result [] file AlreadyGeneratedByNewerHpack

      modifyPackageConfig = writeFile packageConfig $ unlines [
          "name: foo"
        , "version: 0.1.0"
        ]

      modifyCabalFile = do
        xs <- readFile file
        writeFile file $ xs ++ "foo\n"

      manuallyCreateCabalFile = do
        writeFile file "some existing cabal file"

      doesNotGenerateHash :: HasCallStack => GenerateHashStrategy -> Spec
      doesNotGenerateHash strategy = do
        it "does not generate hash" $ do
          hpackWithStrategy strategy `shouldReturn` generated
          readFile file >>= (`shouldNotContain` "hash")

      generatesHash :: HasCallStack => GenerateHashStrategy -> Spec
      generatesHash strategy = do
        it "generates hash" $ do
          hpackWithStrategy strategy `shouldReturn` generated
          readFile file >>= (`shouldContain` "hash")

      doesNotOverwrite :: HasCallStack => GenerateHashStrategy -> Spec
      doesNotOverwrite strategy = do
        it "does not overwrite cabal file" $ do
          existing <- readFile file
          hpackWithStrategy strategy `shouldReturn` modifiedManually
          readFile file `shouldReturn` existing

      with strategy item = context ("with " ++ show strategy) $ item strategy

    context "without an existing cabal file" $ do
      with ForceHash generatesHash
      with PreferHash generatesHash
      with ForceNoHash doesNotGenerateHash
      with PreferNoHash doesNotGenerateHash

    context "with an existing cabal file" $ do
      context "without a hash" $ before_ (hpackWithStrategy ForceNoHash >> modifyPackageConfig) $ do
        with ForceHash generatesHash
        with PreferHash doesNotGenerateHash
        with ForceNoHash doesNotGenerateHash
        with PreferNoHash doesNotGenerateHash

      context "with a hash" $ before_ (hpackWithStrategy ForceHash >> modifyPackageConfig) $ do
        with ForceHash generatesHash
        with PreferHash generatesHash
        with ForceNoHash doesNotGenerateHash
        with PreferNoHash generatesHash

        context "with manual modifications" $ before_ modifyCabalFile $ do
          with ForceHash doesNotOverwrite
          with PreferHash doesNotOverwrite
          with ForceNoHash doesNotGenerateHash
          with PreferNoHash doesNotOverwrite

      context "when created manually" $ before_ manuallyCreateCabalFile $ do
        with ForceHash doesNotOverwrite
        with PreferHash doesNotOverwrite
        with ForceNoHash doesNotOverwrite
        with PreferNoHash doesNotOverwrite

        context "with --force" $ do
          it "overwrites cabal file" $ do
            hpackForce `shouldReturn` generated

      context "when generated with a newer version of hpack" $ do
        it "does not overwrite cabal file" $ do
          _ <- hpackWithVersion [0,22,0]
          old <- readFile file
          modifyPackageConfig
          hpackWithVersion [0,20,0] `shouldReturn` alreadyGeneratedByNewerHpack
          readFile file `shouldReturn` old

      context "when only the hpack version in the cabal file header changed" $ do
        it "does not overwrite cabal file" $ do
          _ <- hpackWithVersion [0,22,0]
          old <- readFile file
          hpackWithVersion [0,30,0] `shouldReturn` outputUnchanged
          readFile file `shouldReturn` old

        it "does not complain if it's newer" $ do
          _ <- hpackWithVersion [0,22,0]
          old <- readFile file
          hpackWithVersion [0,20,0] `shouldReturn` outputUnchanged
          readFile file `shouldReturn` old
