{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ViewPatterns      #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Main where

import           Control.Lens
import           Control.Monad
import           Control.Monad.IO.Class
import           Control.Monad.Trans.AWS
import           Control.Monad.Trans.Class (lift)
import           Data.Conduit            as C
import qualified Data.Conduit.Binary     as CB
import qualified Data.Conduit.List       as CL
import qualified Data.Foldable           as Fold
import           Data.Monoid
import           Control.Monad.Trans.State
import           Data.Text               (Text)
import qualified Data.Text.IO            as Text
import           Network.AWS.Data
import           Network.AWS.S3
import           System.IO
import qualified ClassyPrelude           as CP

main :: IO ()
main = return ()

val :: ToText a => Maybe a -> Text
val = maybe "Nothing" toText

lat :: ObjectVersion -> Text
lat v = maybe mempty (mappend " - " . toText) (v ^. ovIsLatest)

key :: ObjectVersion -> Text
key v = val (v ^. ovKey) <> ": " <> val (v ^. ovVersionId) <> lat v

---------
mySourceList :: Monad m => [a] -> Source m a
mySourceList = mapM_ yield

mySink :: Sink String IO ()
mySink = awaitForever (liftIO . putStrLn)

myAwaitForever :: Monad m => (a -> Conduit a m b) -> Conduit a m b
myAwaitForever f = C.await >>= maybe (return ()) (\w -> f w >> myAwaitForever f)

triple :: Monad m => Conduit a m a
triple = myAwaitForever (\x -> CL.sourceList [x, x, x])

multiplyAll :: Monad m => Conduit Int m Int
multiplyAll = C.await >>= maybe (return ()) (CL.map . (*))


source2 :: Source (State Int) Int
source2 = do
    x <- lift get
    if x <= 0
        then return ()
        else do
            yield x
            lift $ modify (\v -> v - 2)
            source2

conduit2 :: Conduit Int (State Int) (Int, Int)
conduit2 = awaitForever $ \i -> do
    lift $ modify (+ 1)
    x <- lift get
    yield (i, x)

main2 :: IO ()
main2 = do
    let result :: State Int [(Int, Int)]
        result = source2 $$ conduit2 =$ CL.consume
    print $ runState result 5

summingNumbers :: IO ()
summingNumbers = do
  result <- CL.sourceList [1..10 :: Int] $$ CL.fold (+) 0
  print result

copyLocalFile :: IO ()
copyLocalFile = do
  writeFile "input.txt" "This is a test."
  runResourceT $ CB.sourceFile "input.txt" $$ CB.sinkFile "output.txt"
  readFile "output.txt" >>= putStrLn

performTransformation :: IO ()
performTransformation = do
  result <- CL.sourceList [1..10] $$ CL.map (+ 1) =$ CL.consume
  print result

-- myAwaitForever :: Monad m => (i -> Conduit i m o) -> Conduit i m o
-- myAwaitForever f = do
--   Data.Conduit.await >>= maybe (return ()) (\x -> f x >> myAwaitForever f)

myAwaitForeverExample = do
  CL.sourceList [1..10] $$ myAwaitForever (lift . print)

listBucket :: Region -> BucketName -> IO ()
listBucket region bucketName = do
  lgr <- newLogger Debug stdout
  env <- newEnv region Discover <&> envLogger .~ lgr

  runResourceT . runAWST env $ do
    paginate (listObjectVersions bucketName)
      =$= CL.concatMap (view lovrsVersions)
       $$ CL.mapM_     (say . mappend " -> " . key)

fakeListBucket :: Region -> BucketName -> IO ()
fakeListBucket r b =
  putStrLn $ show r ++ " : " ++ show b


listAll :: Region -- ^ Region to operate in.
        -> IO ()
listAll r = do
    lgr <- newLogger Debug stdout
    env <- newEnv r Discover <&> envLogger .~ lgr

    runResourceT . runAWST env $ do
        say "Listing Buckets .."
        bs <- view lbrsBuckets <$> send listBuckets
        say $ "Found " <> toText (length bs) <> " Buckets."

        forM_ bs $ \(view bName -> b) -> do
            say $ "Listing Object Versions in: " <> toText b
            paginate (listObjectVersions b)
                =$= CL.concatMap (view lovrsVersions)
                 $$ CL.mapM_     (say . mappend " -> " . key)

getFile :: Region     -- ^ Region to operate in.
        -> BucketName
        -> ObjectKey  -- ^ The source object key.
        -> FilePath   -- ^ The destination file to save as.
        -> IO ()
getFile r b k f = do
    lgr <- newLogger Debug stdout
    env <- newEnv r Discover <&> envLogger .~ lgr

    runResourceT . runAWST env $ do
        rs <- send (getObject b k)
        view gorsBody rs `sinkBody` CB.sinkFile f
        say $ "Successfully Download: "
            <> toText b <> " - " <> toText k <> " to " <> toText f

syncFile :: Region -> Text -> BucketName -> ObjectKey -> IO ()
syncFile r s b k = do
    lgr <- newLogger Debug stdout
    env <- newEnv r Discover <&> envLogger .~ lgr

    runResourceT . runAWST env $ do
        rs <- send (copyObject b s k)
        liftIO $ putStrLn (show rs)
        return ()
        -- view corsBody rs `sinkBody` CB.sinkFile f
        -- say $ "Successfully Download: "
        --     <> toText b <> " - " <> toText k <> " to " <> toText f

listKeysInBucketPrefix :: Region -> BucketName -> Text -> IO ()
listKeysInBucketPrefix r b p = do
  lgr <- newLogger Debug stdout
  env <- newEnv r Discover <&> envLogger .~ lgr
  runResourceT . runAWST env $ do
    paginate (set lovPrefix (Just p) (listObjectVersions b))
        =$= CL.concatMap (view lovrsVersions)
         $$ CL.mapM_     (say . mappend " -> " . CP.tshow)

putChunkedFile :: Region     -- ^ Region to operate in.
               -> BucketName -- ^ The bucket to store the file in.
               -> ObjectKey  -- ^ The destination object key.
               -> ChunkSize  -- ^ The chunk size to send.
               -> FilePath   -- ^ The source file to upload.
               -> IO ()
putChunkedFile r b k c f = do
    lgr <- newLogger Debug stdout
    env <- newEnv r Discover <&> envLogger .~ lgr

    runResourceT . runAWST env $ do
        bdy <- chunkedFile c f
        void . send $ putObject b k bdy
        say $ "Successfully Uploaded: "
           <> toText f <> " to " <> toText b <> " - " <> toText k

tagBucket :: BucketName          -- ^ Name of the bucket to tag.
          -> [(ObjectKey, Text)] -- ^ List of K/V pairs to apply as tags.
          -> IO ()
tagBucket bkt xs = do
    lgr <- newLogger Debug stdout
    env <- newEnv Ireland Discover <&> envLogger .~ lgr

    let tags = map (uncurry tag) xs
        kv t = toText (t ^. tagKey) <> "=" <> (t ^. tagValue)

    runResourceT . runAWST env $ do
        void . send $ putBucketTagging bkt (tagging & tTagSet .~ tags)
        say $ "Successfully Put Tags: " <> Fold.foldMap kv tags

        ts <- view gbtrsTagSet <$> send (getBucketTagging bkt)
        forM_ ts $ \t ->
            say $ "Found Tag: " <> kv t

say :: MonadIO m => Text -> m ()
say = liftIO . Text.putStrLn

sayKey :: MonadIO m => ObjectVersion -> m ()
sayKey ov = (say . mappend " -> ") (CP.tshow ov)
