content: {-# LANGUAGE ViewPatterns #-}
  
  module Cafe.ChefTodoList
    ( chefTodoListMain
    ) where
  
  import Control.Monad (forM_, unless)
  import Control.Monad.Logger (runNoLoggingT)
  import Data.List (foldl')
  import Data.Map (Map)
  import qualified Data.Map as Map
  import Data.Maybe (catMaybes, mapMaybe)
  import Data.Monoid ((<>))
  import Data.Text (pack)
  import Database.Persist.Sql
  import Database.Persist.Sqlite
  import Options.Applicative
  import System.Console.ANSI (clearScreen, setCursorPosition)
  
  import Eventful
  import Eventful.ReadModel.Memory
  import Eventful.Store.Sqlite
  
  import Cafe.CLI.Options (parseDatabaseFileOption)
  import Cafe.CLI.Transformer
  import Cafe.Models.Tab
  
  -- | Create an in-memory read model that polls the SQLite event store and
  -- updates the chef's todo list.
  chefTodoListMain :: IO ()
  chefTodoListMain = do
    dbFilePath <- execParser $ info (helper <*> parseDatabaseFileOption) (fullDesc <> progDesc "Chef Todo List Terminal")
    pool <- runNoLoggingT $ createSqlitePool (pack dbFilePath) 1
    readModel <- memoryReadModel Map.empty handleChefReadModelEvents
    runPollingReadModel readModel cliGloballyOrderedEventStore (`runSqlPool` pool) 1
  
  handleChefReadModelEvents
    :: Map UUID [Maybe Food]
    -> [GloballyOrderedEvent JSONString]
    -> IO (Map UUID [Maybe Food])
  handleChefReadModelEvents foodMap (map globallyOrderedEventToStoredEvent -> events) = do
    let
      tabEvents = mapMaybe (traverse $ deserialize jsonStringSerializer) events :: [StoredEvent TabEvent]
      foodMap' = foldl' handleEventToMap foodMap $ tabEvents
    unless (null events) $ printFood foodMap'
    return foodMap'
  
  handleEventToMap :: Map UUID [Maybe Food] -> StoredEvent TabEvent -> Map UUID [Maybe Food]
  handleEventToMap foodMap (StoredEvent uuid _ (TabClosed _)) = Map.delete uuid foodMap
  handleEventToMap foodMap storedEvent =
    let
      uuid = storedEventProjectionId storedEvent
      oldList = Map.findWithDefault [] uuid foodMap
    in Map.insert uuid (handleEventToFood oldList $ storedEventEvent storedEvent) foodMap
  
  handleEventToFood :: [Maybe Food] -> TabEvent -> [Maybe Food]
  handleEventToFood oldFood (FoodOrdered newFood) = oldFood ++ map Just newFood
  handleEventToFood oldFood (FoodPrepared indexes) = setIndexesToNothing indexes oldFood
  handleEventToFood food _ = food
  
  printFood :: Map UUID [Maybe Food] -> IO ()
  printFood foodMap = do
    clearScreen
    setCursorPosition 0 0
    putStrLn "Chef's Todo List:"
  
    forM_ (Map.keys foodMap) $ \uuid -> do
      let foodItems = catMaybes $ foodMap Map.! uuid
      unless (null foodItems) $ do
        putStrLn $ "Tab: " ++ show uuid
        forM_ foodItems $ \(Food (MenuItem desc _)) -> putStrLn $ "  - Item: " ++ desc
  