{-# LANGUAGE OverloadedStrings #-}

module Server where

import Control.Concurrent (forkIO, threadDelay)
import Control.Exception (throwIO, throwTo, try)
import Control.Monad (forever, when)
import Control.Monad.Logger (runNoLoggingT, runStderrLoggingT)
import Data.Configurator
import qualified Data.Text as T
import Network.Wai.Handler.Warp (run)
import Servant
import System.Exit
import System.Posix.Signals
import ZcashHaskell.Types (ZebraGetBlockChainInfo(..), ZebraGetInfo(..))
import Zenith.Core (checkBlockChain, checkZebra)
import Zenith.DB (getWallets, initDb, initPool, upgradeAccountTable)
import Zenith.RPC
  ( State(..)
  , ZenithRPC(..)
  , authenticate
  , scanZebra
  , zenithServer
  )
import Zenith.Scanner (rescanZebra)
import Zenith.Types (Config(..))
import Zenith.Utils (getZenithPath)

main :: IO ()
main = do
  config <- load ["$(HOME)/Zenith/zenith.cfg"]
  dbFileName <- require config "dbFileName"
  nodeUser <- require config "nodeUser"
  nodePwd <- require config "nodePwd"
  zebraPort <- require config "zebraPort"
  zebraHost <- require config "zebraHost"
  nodePort <- require config "nodePort"
  currencyCode <- require config "currencyCode"
  dbFP <- getZenithPath
  let dbFilePath = T.pack $ dbFP ++ dbFileName
  let myConfig =
        Config
          dbFilePath
          zebraHost
          zebraPort
          nodeUser
          nodePwd
          nodePort
          currencyCode
  let ctx = authenticate myConfig :. EmptyContext
  w <- try $ checkZebra zebraHost zebraPort :: IO (Either IOError ZebraGetInfo)
  case w of
    Right zebra -> do
      bc <-
        try $ checkBlockChain zebraHost zebraPort :: IO
          (Either IOError ZebraGetBlockChainInfo)
      case bc of
        Left e1 -> throwIO e1
        Right chainInfo -> do
          x <- runNoLoggingT $ initDb dbFilePath
          case x of
            Left e2 -> throwIO $ userError e2
            Right x' -> do
              when x' $ rescanZebra zebraHost zebraPort dbFilePath
              pool <- runNoLoggingT $ initPool dbFilePath
              _ <- runNoLoggingT $ upgradeAccountTable pool
              walList <- getWallets pool $ zgb_net chainInfo
              if not (null walList)
                then do
                  scanThread <-
                    forkIO $
                    forever $ do
                      _ <-
                        scanZebra
                          dbFilePath
                          zebraHost
                          zebraPort
                          (zgb_net chainInfo)
                      threadDelay 90000000
                  putStrLn "Zenith RPC Server 0.9.0.0-beta"
                  putStrLn "------------------------------"
                  putStrLn $
                    "Connected to " ++
                    show (zgb_net chainInfo) ++
                    " Zebra " ++
                    T.unpack (zgi_build zebra) ++ " on port " ++ show zebraPort
                  let myState =
                        State
                          (zgb_net chainInfo)
                          zebraHost
                          zebraPort
                          dbFilePath
                          (zgi_build zebra)
                          (zgb_blocks chainInfo)
                  run nodePort $
                    serveWithContext
                      (Proxy :: Proxy ZenithRPC)
                      ctx
                      (zenithServer myState)
                else putStrLn
                       "No wallets available. Please start Zenith interactively to create a wallet"
