
module Gargantext.API.Server.Named.Public (
  serverPublicGargAPI
  ) where

import Control.Lens ((^?), _Just)
import Data.List qualified as List
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Gargantext.API.Node.File (fileApi)
import Gargantext.API.Prelude (serverError, IsGargServer)
import Gargantext.API.Public.Types (PublicData(..))
import Gargantext.API.Routes.Named.File qualified as Named
import Gargantext.API.Routes.Named.Public qualified as Named
import Gargantext.Core.Utils.DateUtils (utc2year)
import Gargantext.Database.Admin.Types.Hyperdata.Corpus ( hc_fields )
import Gargantext.Database.Admin.Types.Hyperdata.CorpusField
import Gargantext.Database.Admin.Types.Hyperdata.Folder ( HyperdataFolder )
import Gargantext.Database.Admin.Types.Node ( NodeId(..), Node, unNodeId )
import Gargantext.Database.Prelude
import Gargantext.Database.Query.Table.Node.Error (HasNodeError(..))
import Gargantext.Database.Query.Table.NodeNode (selectPublicNodes)
import Gargantext.Database.Schema.Node ( NodePoly(..), node_date, node_hyperdata ) -- (NodePoly(..))
import Gargantext.Prelude
import Servant
import Servant.Server.Generic (AsServerT)

serverPublicGargAPI :: IsGargServer env err m => Text -> Named.GargPublicAPI (AsServerT m)
serverPublicGargAPI baseUrl = Named.GargPublicAPI $
  Named.GargPublicAPI'
    { publicHomeAPI = api_home baseUrl
    , publicNodeAPI = Named.NodeAPI api_node
    }

api_home :: IsGargServer env err m => Text -> Named.HomeAPI (AsServerT m)
api_home baseUrl = Named.HomeAPI $ runDBQuery $ catMaybes
   <$> map (toPublicData baseUrl)
   <$> filterPublicDatas
   <$> selectPublic

api_node :: IsGargServer env err m => NodeId -> Named.FileAPI (AsServerT m)
api_node nId = Named.FileAPI $ do
  pubNodes <- runDBQuery publicNodes
  -- TODO optimize with SQL
  case Set.member nId pubNodes of
    False -> serverError $ err405 { errBody = "Not allowed" }
    True  -> fileApi nId

-------------------------------------------------------------------------


selectPublic :: HasNodeError err
             => DBQuery err x [( Node HyperdataFolder, Maybe Int)]
selectPublic = selectPublicNodes

  -- For tests only
  -- pure $ replicate 6 defaultPublicData

filterPublicDatas :: [(Node HyperdataFolder, Maybe Int)]
                  -> [(Node HyperdataFolder, [NodeId])]
filterPublicDatas datas =
  map (\(n,mi) ->
          let mi' = UnsafeMkNodeId <$> mi in
                  ( _node_id n, (n, maybe [] (:[]) mi' ))
      ) datas
      & Map.fromListWith (\(n1,i1) (_n2,i2) -> (n1, i1 <> i2))
      & Map.filter (not . null . snd)
      & Map.elems

publicNodes :: HasNodeError err
            => DBQuery err x (Set NodeId)
publicNodes = do
  candidates <- filterPublicDatas <$> selectPublicNodes
  pure $ Set.fromList
       $ List.concat
       $ map (\(n, ns) -> (_node_id n) : ns) candidates


-- http://localhost:8008/api/v1.0/node/23543/file/download<Paste>
-- http://localhost:8000/images/Gargantextuel-212x300.jpg
toPublicData :: Text -> (Node HyperdataFolder, [NodeId]) -> Maybe PublicData
toPublicData base (n , mn) = do
  title <- (hd ^? (_Just . hf_data . cf_title))
  abstract <- (hd ^? (_Just . hf_data . cf_desc ))
  img <- (Just $ url' mn) -- "images/Gargantextuel-212x300.jpg"
  url <- (Just $ url' mn)
  date <- Just (show $ utc2year (n^.node_date))
  database <- (hd ^? (_Just . hf_data . cf_query))
  author <- (hd ^? (_Just . hf_data . cf_authors))
  pure $ PublicData { .. }
  where
    hd = head
       $ filter (\(HyperdataField cd _ _) -> cd == JSON)
       $ n^. (node_hyperdata . hc_fields)
    url' :: [NodeId] -> Text
    url' mn' = base
           <>   "/public/"
           <> (show $ (maybe 0 unNodeId $ head mn'))
           <> "/file/download"
