{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes       #-}

module Main where

import           Test.Hspec
import           Test.Mockery.Directory

import           Control.Exception
import           Control.Monad                (when)
import           Data.List                    (intercalate)
import           Data.String.Interpolate
import           Data.String.Interpolate.Util
import           Elm.Versions (ElmVersion(..))
import           Servant.Elm
import           System.Directory             (canonicalizePath,
                                               createDirectoryIfMissing,
                                               doesDirectoryExist,
                                               getCurrentDirectory, removeFile,
                                               setCurrentDirectory)
import           System.Process (callCommand)
import           System.Process.Typed
import qualified Data.Text as T
import qualified Elm.Module                   as Elm
import           Data.Text.IO                 as TIO

import Common (Book, TestApi)

main :: IO ()
main =
  hspec spec

spec :: Test.Hspec.Spec
spec = do
  describe "generateElmForAPI" $ do
    it "creates compilable javascript" $ do
      inTempElmDir $ do
        let options = defElmOptions
            namespace :: [String]
            namespace = ["Generated", "BooksApi"]
            imports = defElmImports
            rootDir = "."
            typeDefs = [ DefineElm (Proxy :: Proxy Book)
                       ]
            api = (Proxy :: Proxy TestApi)
        let out =
              T.unlines $
              [ T.pack $ Elm.moduleHeader Elm0p19 moduleName
              , ""
              , imports
              , ""
              , "type NoContent = NoContent"
              , ""
              , T.pack $ Elm.makeModuleContentWithAlterations (elmAlterations options) typeDefs
              ] ++
              generateElmForAPIWith options api
            moduleName = T.unpack (T.intercalate "." (map T.pack namespace))
            filePath = intercalate "/" $ rootDir:init namespace
            fileName = intercalate "/" $ filePath : [last namespace ++ ".elm"]
        createDirectoryIfMissing True filePath
        TIO.writeFile fileName out

        -- -- Useful for locally checking out sources in your tmp dir
        -- callCommand "cp -r . /home/kb/tmp/servelmtest"
        runProcess_ "elm make Generated/BooksApi.elm --output api.js"


inTempElmDir :: IO a -> IO a
inTempElmDir action = do
  callCommand "rm -rf ./_test-cache"
  cacheExists <- doesDirectoryExist "_test-cache"
  when (not cacheExists) createCache
  cacheDir <- canonicalizePath "_test-cache"
  inTempDirectory $ do
    callCommand ("cp -r " ++ cacheDir ++ "/* .")
    action

createCache :: IO ()
createCache = do
  createDirectoryIfMissing True "_test-cache"
  withCurrentDirectory "_test-cache" $ do
    TIO.writeFile "elm.json" $ T.pack $ unindent $ [i|
      {
          "type": "application",
          "source-directories": [
              "."
          ],
          "elm-version": "0.19.0",
          "version": "1.0.0",
          "summary": "helpful summary of your project, less than 80 characters",
          "dependencies": {
              "direct": {
                "elm/core": "1.0.2",
                "elm/json": "1.1.3",
                "elm/http": "2.0.0",
                "elm/url": "1.0.0",
                "bartavelle/json-helpers": "2.0.2"
              },
              "indirect": {
                 "elm/bytes": "1.0.8",
                 "elm/file": "1.0.5",
                 "elm/time": "1.0.0"
              }
          },
          "test-dependencies": {
            "direct": {},
            "indirect": {}
          }
      }
    |]
    -- callCommand "elm install"
    -- compileDependencies

compileDependencies :: IO ()
compileDependencies = do
  TIO.writeFile "Main.elm" "module Main exposing (foo)\n\nfoo : Int\nfoo = 42\n"
  callCommand "echo '>>>>>> Main.elm'"
  callCommand "cat Main.elm"
  callCommand "elm make Main.elm --output main.js"
  removeFile "Main.elm"
  removeFile "main.js"

withCurrentDirectory :: FilePath -> IO a -> IO a
withCurrentDirectory dir action =
  bracket enter recover (const action)
  where
    enter = do
      original <- getCurrentDirectory
      setCurrentDirectory dir
      return original
    recover = setCurrentDirectory
