{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Faker.Provider.TH where
import Config
import Control.Monad.Catch
import Control.Monad.IO.Class
import Data.Char (toUpper)
import Data.Map.Strict (Map)
import Data.Monoid ((<>))
import Data.Text (Text, unpack)
import qualified Data.Text as T
import Data.Vector (Vector)
import Data.Yaml
import Faker
import Faker.Internal
import Language.Haskell.TH
textTitle :: Text -> Text
textTitle txt =
  case T.uncons txt of
    Nothing -> txt
    Just (c, rem) -> T.cons (toUpper c) rem
genParser ::
     Text 
  -> Text 
  -> Q [Dec]
genParser entityName fieldName = do
  let funName =
        mkName $
        unpack $
        "parse" <> (refinedText $ textTitle entityName) <>
        (refinedText $ textTitle fieldName)
  let parserFnName =
        unpack $ "parse" <> (refinedText $ textTitle entityName) <> "Field"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  let tvA = mkName "a"
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [AppT (ConT ''FromJSON) (VarT tvA), AppT (ConT ''Monoid) (VarT tvA)]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT
                 (AppT ArrowT (ConT ''Value))
                 (AppT (ConT ''Parser) (VarT tvA)))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE (VarE parserFn) (VarE tsettings))
                  (LitE (StringL (unpack fieldName)))))
            []
        ]
    ]
genParsers ::
     Text 
  -> [Text] 
  -> Q [Dec]
genParsers entityName fieldName = do
  let fieldNames = map textTitle fieldName
      fieldNames' = refinedText $ T.concat fieldNames
      funName =
        mkName $
        unpack $
        "parse" <> (refinedText $ textTitle entityName) <> (fieldNames')
  let parserFnName =
        unpack $ "parse" <> (refinedText $ textTitle entityName) <> "Fields"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  let tvA = mkName "a"
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [AppT (ConT ''FromJSON) (VarT tvA), AppT (ConT ''Monoid) (VarT tvA)]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT
                 (AppT ArrowT (ConT ''Value))
                 (AppT (ConT ''Parser) (VarT tvA)))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE (VarE parserFn) (VarE tsettings))
                  (ListE (map (\x -> LitE (StringL (unpack x))) fieldName))))
            []
        ]
    ]
genProviders ::
     Text 
  -> [Text] 
  -> Q [Dec]
genProviders entityName fieldName = do
  let fieldNames = map textTitle fieldName
      fieldNames' = refinedText $ T.concat fieldNames
      funName =
        mkName $ unpack $ (refinedText entityName) <> fieldNames' <> "Provider"
      tvM = mkName "m"
      parserFnName =
        unpack $ "parse" <> (refinedText $ textTitle entityName) <> fieldNames'
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [ AppT (ConT ''MonadThrow) (VarT tvM)
           , AppT (ConT ''MonadIO) (VarT tvM)
           ]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT (VarT tvM) (AppT (ConT ''Vector) (ConT ''Text)))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE
                     (AppE (VarE 'fetchData) (VarE tsettings))
                     (ConE (mapSource entityName)))
                  (VarE parserFn)))
            []
        ]
    ]
genProvidersSingle ::
     Text 
  -> [Text] 
  -> Q [Dec]
genProvidersSingle entityName fieldName = do
  let fieldNames = map textTitle fieldName
      fieldNames' = refinedText $ T.concat fieldNames
      funName =
        mkName $ unpack $ (refinedText entityName) <> fieldNames' <> "Provider"
      tvM = mkName "m"
      parserFnName =
        unpack $ "parse" <> (refinedText $ textTitle entityName) <> fieldNames'
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [ AppT (ConT ''MonadThrow) (VarT tvM)
           , AppT (ConT ''MonadIO) (VarT tvM)
           ]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT (VarT tvM) (AppT (ConT ''Vector) (ConT ''Text)))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE
                     (AppE (VarE 'fetchDataSingle) (VarE tsettings))
                     (ConE (mapSource entityName)))
                  (VarE parserFn)))
            []
        ]
    ]
genProvidersSingleUnresolved ::
     Text 
  -> [Text] 
  -> Q [Dec]
genProvidersSingleUnresolved entityName fieldName = do
  let fieldNames = map textTitle fieldName
      fieldNames' = refinedText $ T.concat fieldNames
      funName =
        mkName $ unpack $ (refinedText entityName) <> fieldNames' <> "Provider"
      tvM = mkName "m"
      parserFnName =
        unpack $ "parse" <> (refinedText $ textTitle entityName) <> fieldNames' <> "Unresolved"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [ AppT (ConT ''MonadThrow) (VarT tvM)
           , AppT (ConT ''MonadIO) (VarT tvM)
           ]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT (VarT tvM) (AppT (ConT ''Unresolved) (ConT ''Text))))) 
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE
                     (AppE (VarE 'fetchData) (VarE tsettings))
                     (ConE (mapSource entityName)))
                  (VarE parserFn)))
            []
        ]
    ]
genProvider ::
     Text 
  -> Text 
  -> Q [Dec]
genProvider entityName fieldName = do
  let funName =
        mkName $
        unpack $
        (refinedText entityName) <> (refinedText $ textTitle fieldName) <>
        "Provider"
      tvM = mkName "m"
      parserFnName =
        unpack $
        "parse" <> (refinedText $ textTitle entityName) <>
        (refinedText $ textTitle fieldName)
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [ AppT (ConT ''MonadThrow) (VarT tvM)
           , AppT (ConT ''MonadIO) (VarT tvM)
           ]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT (VarT tvM) (AppT (ConT ''Vector) (ConT ''Text)))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE
                     (AppE (VarE 'fetchData) (VarE tsettings))
                     (ConE (mapSource entityName)))
                  (VarE parserFn)))
            []
        ]
    ]
genParserUnresolved :: Text -> Text -> Q [Dec]
genParserUnresolved entityName fieldName = do
  let funName =
        mkName $
        unpack $
        "parse" <> (refinedText $ textTitle entityName) <>
        (refinedText $ textTitle fieldName) <>
        "Unresolved"
  let parserFnName =
        unpack $
        "parseUnresolved" <> (refinedText $ textTitle entityName) <> "Field"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  let tvA = mkName "a"
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [AppT (ConT ''FromJSON) (VarT tvA), AppT (ConT ''Monoid) (VarT tvA)]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT
                 (AppT ArrowT (ConT ''Value))
                 (AppT (ConT ''Parser) (AppT (ConT ''Unresolved) (VarT tvA))))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE (VarE parserFn) (VarE tsettings))
                  (LitE (StringL (unpack fieldName)))))
            []
        ]
    ]
genParserSingleUnresolved :: Text -> Text -> Q [Dec]
genParserSingleUnresolved entityName fieldName = do
  let funName =
        mkName $
        unpack $
        "parse" <> (refinedText $ textTitle entityName) <>
        (refinedText $ textTitle fieldName) <>
        "Unresolved"
  let parserFnName =
        unpack $
        "parseUnresolved" <> (refinedText $ textTitle entityName) <> "Field"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  let tvA = mkName "a"
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           []
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT
                 (AppT ArrowT (ConT ''Value))
                 (AppT (ConT ''Parser) (AppT (ConT ''Unresolved) (ConT ''Text))))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE (VarE parserFn) (VarE tsettings))
                  (LitE (StringL (unpack fieldName)))))
            []
        ]
    ]
genParserUnresolveds :: Text -> [Text] -> Q [Dec]
genParserUnresolveds entityName fieldNames = do
  let fieldNames' = refinedText $ T.concat $ map textTitle fieldNames
      funName =
        mkName $
        unpack $
        "parse" <> (refinedText $ textTitle entityName) <> fieldNames' <>
        "Unresolved"
  let parserFnName =
        unpack $
        "parseUnresolved" <> (refinedText $ textTitle entityName) <> "Fields"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  let tvA = mkName "a"
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [AppT (ConT ''FromJSON) (VarT tvA), AppT (ConT ''Monoid) (VarT tvA)]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT
                 (AppT ArrowT (ConT ''Value))
                 (AppT (ConT ''Parser) (AppT (ConT ''Unresolved) (VarT tvA))))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE (VarE parserFn) (VarE tsettings))
                  (ListE (map (\x -> LitE (StringL (unpack x))) fieldNames))))
            []
        ]
    ]
genProviderUnresolveds ::
     Text 
  -> [Text] 
  -> Q [Dec]
genProviderUnresolveds entityName fieldNames = do
  let fieldNames' = refinedText $ T.concat $ map textTitle fieldNames
      entityName' = refinedText entityName
      funName = mkName $ unpack $ entityName' <> fieldNames' <> "Provider"
      tvM = mkName "m"
      parserFnName =
        unpack $
        "parse" <> (textTitle entityName') <> fieldNames' <> "Unresolved"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [ AppT (ConT ''MonadThrow) (VarT tvM)
           , AppT (ConT ''MonadIO) (VarT tvM)
           ]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT
                 (VarT tvM)
                 (AppT (ConT ''Unresolved) (AppT (ConT ''Vector) (ConT ''Text))))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE
                     (AppE (VarE 'fetchData) (VarE tsettings))
                     (ConE (mapSource entityName)))
                  (VarE parserFn)))
            []
        ]
    ]
genProviderUnresolved ::
     Text 
  -> Text 
  -> Q [Dec]
genProviderUnresolved entityName fieldName = do
  let funName =
        mkName $
        unpack $ refinedText $ entityName <> (textTitle fieldName) <> "Provider"
      tvM = mkName "m"
      parserFnName =
        unpack $
        "parse" <> (refinedText $ textTitle entityName) <>
        (refinedText $ textTitle fieldName) <>
        "Unresolved"
  parserName <- lookupValueName parserFnName
  parserFn <-
    case parserName of
      Nothing -> fail $ "Faker.TH: Didn't find function " <> parserFnName
      Just fn -> return fn
  tsettings <- newName "settings"
  return $
    [ SigD
        funName
        (ForallT
           []
           [ AppT (ConT ''MonadThrow) (VarT tvM)
           , AppT (ConT ''MonadIO) (VarT tvM)
           ]
           (AppT
              (AppT ArrowT (ConT ''FakerSettings))
              (AppT
                 (VarT tvM)
                 (AppT (ConT ''Unresolved) (AppT (ConT ''Vector) (ConT ''Text))))))
    , FunD
        funName
        [ Clause
            [VarP tsettings]
            (NormalB
               (AppE
                  (AppE
                     (AppE (VarE 'fetchData) (VarE tsettings))
                     (ConE (mapSource entityName)))
                  (VarE parserFn)))
            []
        ]
    ]
genAppParser :: Text -> Q [Dec]
genAppParser = genParser "app"
genAppProvider :: Text -> Q [Dec]
genAppProvider = genProvider "app"
genAppParserUnresolved :: Text -> Q [Dec]
genAppParserUnresolved = genParserUnresolved "app"
genAppProviderUnresolved :: Text -> Q [Dec]
genAppProviderUnresolved = genProviderUnresolved "app"