-- |The basic scrape function. It fires a GET request against the url defined within the provided
-- 'ScrapeConfig'. The body is passed to the provided 'Scraper'. The result 'Data.Text.Lazy.Text' of the
-- latter is used to determine whether something has changed on the respective website. If so, the callback
-- configured in 'ScrapeConfig' is executed and 'CallbackCalled' is returned. Otherwise 'CallbackNotCalled' is
-- returned.
scrape :: ScrapeConfig -> Scraper -> Either [ValidationError] (IO ScrapeResult)
scrape sc s = let result = scrapeOrchestration <$ validateScrapeConfig sc
              in result ^. Validation._Either
  where scrapeOrchestration = 
          let responseBody = (^. Http.responseBody)
              urlToRequest = sc ^. scrapeInfoUrl
              requestLog = Log.infoM loggerName $ "Requesting " ++ urlToRequest
              request = (s . responseBody <$>) . Http.get
              response = (request urlToRequest <* requestLog) `Exception.catch` httpExceptionHandler sc
          in do (response', latestHashedResponse) <- Async.concurrently response (readLatestHash sc)
                let currentHashedResponse = hash' response'
                let executeCallbackConfig' = executeCallbackConfig sc response'
                let saveHash'' = saveHash' currentHashedResponse urlToRequest
                let saveHashAndExecuteCallbackConfig = Async.concurrently  saveHash'' executeCallbackConfig'

                if hashesAreDifferent latestHashedResponse currentHashedResponse 
                then CallbackCalled <$ saveHashAndExecuteCallbackConfig 
                else pure CallbackNotCalled 

        hashesAreDifferent :: LatestHash -> CurrentHash -> Bool
        hashesAreDifferent latestHash currentHash = 
          Maybe.isNothing latestHash || Foldable.or ((/= currentHash) <$> latestHash) 

        saveHash' :: Hash -> Url -> IO ()
        saveHash' h url = let saveHashMsg = "Saved new hash for url '" ++ url ++ "'"
                              saveHashLog = Log.infoM loggerName saveHashMsg
                          in  saveHash sc h <* saveHashLog
scrape :: ScrapeConfig -> Scraper -> Either [ValidationError] (IO ScrapeResult)
scrape sc s = let result = scrapeOrchestration <$ validateScrapeConfig sc
              in result ^. Validation._Either
  where scrapeOrchestration = 
          let responseBody = (^. Http.responseBody)
              urlToRequest = sc ^. scrapeInfoUrl
              requestLog = Log.infoM loggerName $ "Requesting " ++ urlToRequest
              request = (s . responseBody <$>) . Http.get
              response = (request urlToRequest <* requestLog) `Exception.catch` httpExceptionHandler sc
          in do (response', latestHashedResponse) <- Async.concurrently response (readLatestHash sc)
                let currentHashedResponse = hash' response'
                let executeCallbackConfig' = executeCallbackConfig sc response'
                let saveHash'' = saveHash' currentHashedResponse urlToRequest
                let saveHashAndExecuteCallbackConfig = Async.concurrently  saveHash'' executeCallbackConfig'

                if hashesAreDifferent latestHashedResponse currentHashedResponse 
                then CallbackCalled <$ saveHashAndExecuteCallbackConfig 
                else pure CallbackNotCalled 

        hashesAreDifferent :: LatestHash -> CurrentHash -> Bool
        hashesAreDifferent latestHash currentHash = 
          Maybe.isNothing latestHash || Foldable.or ((/= currentHash) <$> latestHash) 

        saveHash' :: Hash -> Url -> IO ()
        saveHash' h url = let saveHashMsg = "Saved new hash for url '" ++ url ++ "'"
                              saveHashLog = Log.infoM loggerName saveHashMsg
                          in  saveHash sc h <* saveHashLog
scrape sc s = let result = scrapeOrchestration <$ validateScrapeConfig sc
              in result ^. Validation._Either
  where scrapeOrchestration = 
          let responseBody = (^. Http.responseBody)
              urlToRequest = sc ^. scrapeInfoUrl
              requestLog = Log.infoM loggerName $ "Requesting " ++ urlToRequest
              request = (s . responseBody <$>) . Http.get
              response = (request urlToRequest <* requestLog) `Exception.catch` httpExceptionHandler sc
          in do (response', latestHashedResponse) <- Async.concurrently response (readLatestHash sc)
                let currentHashedResponse = hash' response'
                let executeCallbackConfig' = executeCallbackConfig sc response'
                let saveHash'' = saveHash' currentHashedResponse urlToRequest
                let saveHashAndExecuteCallbackConfig = Async.concurrently  saveHash'' executeCallbackConfig'

                if hashesAreDifferent latestHashedResponse currentHashedResponse 
                then CallbackCalled <$ saveHashAndExecuteCallbackConfig 
                else pure CallbackNotCalled 

        hashesAreDifferent :: LatestHash -> CurrentHash -> Bool
        hashesAreDifferent latestHash currentHash = 
          Maybe.isNothing latestHash || Foldable.or ((/= currentHash) <$> latestHash) 

        saveHash' :: Hash -> Url -> IO ()
        saveHash' h url = let saveHashMsg = "Saved new hash for url '" ++ url ++ "'"
                              saveHashLog = Log.infoM loggerName saveHashMsg
                          in  saveHash sc h <* saveHashLog