content: {-# LANGUAGE OverloadedStrings #-}
  module Main where
  
  import Control.Applicative
  import Data.Text (Text)
  import qualified Data.Text as Text
  import Data.HashMap.Strict (HashMap)
  import qualified Data.HashMap.Strict as HashMap
  
  import Telegram.Bot.API
  import Telegram.Bot.Simple
  import Telegram.Bot.Simple.UpdateParser
  
  type Item = Text
  
  data Model = Model
    { todoLists   :: HashMap Text [Item]
    , currentList :: Text
    }
  
  defaultListName :: Text
  defaultListName = "Default"
  
  initialModel :: Model
  initialModel = Model
    { todoLists = HashMap.fromList [(defaultListName, [])]
    , currentList = defaultListName
    }
  
  data Action
    =  Start
    | AddItem Item
    | RemoveItem Item
    | SwitchToList Text
    | ShowAll
    | Show Text
    deriving (Show, Read)
  
  todoBot3 :: BotApp Model Action
  todoBot3 = BotApp
    { botInitialModel = initialModel
    , botAction = flip updateToAction
    , botHandler = handleAction
    , botJobs = []
    }
    where
      updateToAction :: Model -> Update -> Maybe Action
      updateToAction _ = parseUpdate $
            AddItem      <$> plainText
        <|> Start        <$  command "start"
        <|> AddItem      <$> command "add"
        <|> RemoveItem   <$> command "remove"
        <|> SwitchToList <$> command "switch_to_list"
        <|> Show         <$> command "show"
        <|> ShowAll      <$  command "show_all"
        <|> callbackQueryDataRead
  
      handleAction :: Action -> Model -> Eff Action Model
      handleAction action model = case action of
        Start -> model <# do
          reply (toReplyMessage startMessage)
            { replyMessageReplyMarkup = Just (SomeReplyKeyboardMarkup startKeyboard) }
        AddItem item -> addItem item model <# do
          replyText "Ok, got it!"
        RemoveItem item -> removeItem item model <# do
          replyText "Item removed!"  
        SwitchToList name -> model { currentList = name } <# do
          replyText ("Switched to list «" <> name <> "»!") 
        ShowAll -> model <# do
          reply (toReplyMessage "Available todo lists")
            { replyMessageReplyMarkup = Just (SomeInlineKeyboardMarkup listsKeyboard) }
        Show "" -> model <# do
          return (Show defaultListName)
        Show name -> model <# do
          let items = concat (HashMap.lookup name (todoLists model))
          if null items
            then reply (toReplyMessage ("The list «" <> name <> "» is empty. Maybe try these starter options?"))
                   { replyMessageReplyMarkup = Just (SomeReplyKeyboardMarkup startKeyboard) }
            else replyText (Text.unlines items)
  
        where
          listsKeyboard = InlineKeyboardMarkup
            (map (\name -> [actionButton name (Show name)]) (HashMap.keys (todoLists model)))
  
      startMessage = Text.unlines
        [ "Hello! I am your personal TODO bot :)"
        , ""
        , "You can add new items to your todo list just by typing it!"
        , "You can also use /add command to do that explicitely."
        , "To remove an item use /remove command."
        , ""
        , "You can manage multiple todo lists:"
        , "Switch to a new named list with /switch_to_list <list>."
        , "Show all available lists with /show_all."
        , "Show items for a specific list with /show <list>."
        , ""
        , "Here are some starter options, try adding something to the list."
        ]
  
      startKeyboard :: ReplyKeyboardMarkup
      startKeyboard = ReplyKeyboardMarkup
        { replyKeyboardMarkupKeyboard =
            [ [ "Buy milk", "Get job done" ]
            , [ "Build a house", "Plant a tree" ]
            ]
        , replyKeyboardMarkupResizeKeyboard = Just True
        , replyKeyboardMarkupOneTimeKeyboard = Just True
        , replyKeyboardMarkupSelective = Nothing
        , replyKeyboardMarkupInputFieldSelector = Nothing
        , replyKeyboardMarkupIsPersistent = Nothing
        }
  
  addItem :: Item -> Model -> Model
  addItem item model = model
    { todoLists = HashMap.insertWith (++) (currentList model) [item] (todoLists model) }
  
  removeItem :: Item -> Model -> Model
  removeItem item model = model
    { todoLists = HashMap.adjust (filter (/= item)) (currentList model) (todoLists model)}
  
  run :: Token -> IO ()
  run token = do
    env <- defaultTelegramClientEnv token
    startBot_ (conversationBot updateChatId todoBot3) env
  
  main :: IO ()
  main = do
    putStrLn "Please, enter Telegram bot's API token:"
    token <- Token . Text.pack <$> getLine
    run token
  