module Lib where

import Types
import JSONInstances
import AWSCommands

import Data.List (nub, sort, delete)
import Data.Map (Map)
import qualified Data.Map as Map

outputDeletionPlan :: String -> IO Dependency
outputDeletionPlan stackName = do
  putStrLn $ "Retrieving dependencies of " ++ stackName ++ "..."
  dag <- buildDependencyGraph (StackName stackName)
  putStrLn "Done.  Deletion order:\n"
  mapM_ print $ deletionOrder dag
  return dag

showDeletionPlan :: String -> IO ()
showDeletionPlan stackName = do
  dag <- outputDeletionPlan stackName
  putStrLn "\nIf you trust this app you can execute:"
  putStrLn $ "forest-fire \"" ++ stackName ++ "\" --delete\n"

actuallyDoTheDelete :: String -> IO ()
actuallyDoTheDelete stackName = do
  dag <- outputDeletionPlan stackName
  putStrLn "Deleting dependencies and stack..."
  mapM_ doDeletionWait $ deletionOrder dag

deletionOrder :: Dependency -> [StackName]
deletionOrder d | d == Map.empty = []
deletionOrder d | null (safe d) = error "bork bork circular dependencies!"
deletionOrder d = safe d ++ deletionOrder (without (safe d) d)
  where
    without :: [StackName] -> Dependency -> Dependency
    without [] d = d
    without (stack : ss) d = Map.map (delete stack) $ Map.delete stack (without ss d)

safe :: Dependency -> [StackName]
safe d = Map.keys $ Map.filter null d

buildDependencyGraph :: AWSExecution m => StackName -> m Dependency
buildDependencyGraph name = do
    outputs <- findExportsByStack name
    importers <- mapM whoImportsThisValue outputs
    let children = sort $ nub $ concat importers
    downstreamDeps <- mapM buildDependencyGraph children
    pure $ Map.unionsWith (\new old->nub ((++) new old))
                          (downstreamDeps ++ [Map.singleton name children])