{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

import Control.Exception (throw)
import Data.Aeson (withObject)
import Data.ByteString (readFile)
import Data.Monoid
import Data.Yaml
import Development.Shake
import Development.Shake.Command
import Development.Shake.FilePath
import Development.Shake.Util
import Prelude hiding (readFile)

main :: IO ()
main = do
  config <- parseConfig "maintain.settings.yml"
  case config of
    Nothing -> putStrLn "parsing failed"
    Just x  -> shakeIt x

shakeIt conf = do
  let linux = head $ byAttrVal osName "Linux" $ infra $ conf
      osx   = head $ byAttrVal osName "OSX" $ infra $ conf

      local      = head $ byAttrVal sType "LOCAL" $ env $ conf
      test       = head $ byAttrVal sType "TEST"  $ env $ conf
      production = head $ byAttrVal sType "PRODUCTION" $ env $ conf

      byAttrVal attr val = filter ((==val) . attr)

  shakeArgs shakeOptions{shakeFiles="_build"} $ do
    -- want ["_build/osx/" <.> exe]

    phony "clean" $ do
        putNormal "Cleaning files in _build"
        removeFilesAfter "_build" ["//*"]

    phony "clean-osx" $ osCleanAction osx
    phony "build-osx" $ osBuildAction osx local

    phony "clean-linux" $ osCleanAction linux
    phony "build-linux-test" $ osBuildAction linux test
    phony "build-linux-prod" $ osBuildAction linux production

    phony "deploy-test" $ osDeployAction test
    phony "deploy-prod" $ osDeployAction production

data Infrastructure =
  Infrastructure
    { osName :: String
    , osConfig :: String
    , osPath :: String
    }

data Server =
  Server
    { sType :: String
    , sRobots :: String
    , sConfig :: String
    , sRemote :: String
    , sRemotePath :: String
    , distribPath :: String
    }

data Settings = Settings { infra :: [Infrastructure], env :: [Server] }

instance FromJSON Infrastructure where
  parseJSON = withObject "Infrastructure" $ \o -> do
    osName   <- o .: "osName"
    osConfig <- o .: "osConfig"
    osPath   <- o .: "osPath"
    return Infrastructure {..}

instance FromJSON Server where
  parseJSON = withObject "Server" $ \o -> do
    sType       <- o .: "sType"
    sRobots     <- o .: "sRobots"
    sConfig     <- o .: "sConfig"
    sRemote     <- o .: "sRemote"
    sRemotePath <- o .: "sRemotePath"
    distribPath <- o .: "distribPath"
    return Server {..}

instance FromJSON Settings where
  parseJSON = withObject "Settings" $ \o -> do
    infra <- o .: "infrastructure"
    env   <- o .: "server"
    return Settings {..}

parseConfig :: String -> IO (Maybe Settings)
parseConfig fn = do
  t <- readFile fn
  return $ either throw id $ decodeEither' t

osCleanAction x = do
  putNormal $ "Cleaning .stack files for " <> (osName x)
  cmd $ "stack --stack-yaml " <> (osConfig x) <> " clean"

osBuildAction x target = do
  putNormal $ "OS: " <> (osName x)
  putNormal $ "Environment: " <> (sType target)
  () <- cmd $ "mkdir -p " <> (distribPath target)
  () <- cmd $ "cp config/" <> (sRobots target) <> " config/robots.txt"
  () <- cmd $ "cp config/" <> (sConfig target) <> " config/settings.yml"
  putNormal $ "Configs installed"
  putNormal $ "Building from sources..."
  () <- cmd $ "stack --stack-yaml " <> (osConfig x) <> " build"
  putNormal "Copying executable"
  () <- cmd $ "cp " <> (osPath x) <> " ./carbuyer-advisor "
  let zipcmd = "zip -r " <> (distribPath target) <> "/dist carbuyer-advisor pcookies pjs/* static/* config/* *.sh tmp/*"
      zipcmd :: String

  cmd zipcmd


osDeployAction target = do
  putNormal $ "Deploying to " <> (sType target)
  () <- cmd $ "scp " <> (distribPath target) <> "/dist.zip " <> (sRemote target) <> ":" <> (sRemotePath target) <> ""
  () <- cmd $ "rm " <> (distribPath target) <> "/dist.zip"
  cmd $ "ssh " <> (sRemote target) <> " cd " <> (sRemotePath target) <> " ; ./unzip.sh"
