#!/usr/bin/env stack
-- stack runghc --package libyaml --package aeson --package unordered-containers --package text

{-# LANGUAGE OverloadedStrings
           , LambdaCase
           , PackageImports
           , NamedFieldPuns
           , RecordWildCards
  #-}

import "libyaml" Text.Libyaml
import Control.Monad.Trans.Resource
import Data.Conduit
import qualified Data.Conduit.List as C

import qualified Data.Aeson as JSON

import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as CBS
import qualified Data.ByteString.Lazy as LBS

import qualified Data.HashMap.Lazy as HashMap

import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import qualified Data.Text.Encoding as Text

import Text.Printf
import System.IO (stderr)

main :: IO ()
main = do
  Just packageJSON <- JSON.decode <$> LBS.readFile "package.json"
  let
    newVersion :: Text
    Just (JSON.String newVersion) = HashMap.lookup ("version" :: Text) packageJSON

  updatePackageYaml newVersion


updatePackageYaml :: Text -> IO ()
updatePackageYaml newVersion = do
  (oldVersion, start, end) <- runResourceT . runConduit . (.|) (decodeFileMarked "package.yaml") $ do
    awaitUntil $ \case
      MarkedEvent{ yamlEvent = EventMappingStart _ _ _ } -> True
      _ -> False
    awaitUntil $ \case
      MarkedEvent{ yamlEvent = EventScalar s _ _ _ }
        | s == "version" -> True
      _ -> False
    _ <- await -- Throw away "version: "
    Just MarkedEvent{ yamlEvent = EventScalar oldVersion' _ _ _, .. } <- await
    let oldVersion = Text.decodeUtf8 oldVersion'
    return (oldVersion, yamlStartMark, yamlEndMark)

  encNewVersion <- runResourceT . runConduit . (.| encode) $ C.sourceList
    [ EventStreamStart
    , EventDocumentStart
    , EventScalar (Text.encodeUtf8 newVersion) NoTag Any Nothing
    , EventDocumentEnd
    , EventStreamEnd
    ]

  hPrintf stderr "package.yaml: %s -> %s\n" oldVersion newVersion

  packageYaml <- BS.readFile "package.yaml"
  BS.writeFile "package.yaml" . mconcat $
    [ BS.take (fromIntegral $ yamlIndex start) packageYaml
    , Text.encodeUtf8 . Text.strip $ Text.decodeUtf8 encNewVersion
    , BS.drop (fromIntegral $ yamlIndex end) packageYaml
    ]
  where
    awaitUntil :: Monad m => (i -> Bool) -> ConduitM i o m ()
    awaitUntil pred = do
      nextIn <- await
      case nextIn of
        Nothing -> error "Ran out of input in awaitUntil"
        Just inp
          | pred inp -> leftover inp
        Just _ -> awaitUntil pred
