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

import Control.Lens ((^?), _Just)
import Data.Map.Strict qualified as Map
import Data.Set qualified as Set
import Data.Text qualified as T
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 ( ServerError(errBody), err405 )
import Servant.Client.Core.BaseUrl (BaseUrl(..), showBaseUrl)
import Servant.Server.Generic (AsServerT)

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

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

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

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


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, maybeToList 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
       $ concatMap (\(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 :: BaseUrl -> (Node HyperdataFolder, [NodeId]) -> Maybe PublicData
toPublicData baseUrl (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)
    path :: [NodeId] -> Text
    path mn' = "/api/v1.0"
           <>   "/public/"
           <> show (maybe 0 unNodeId (head mn'))
           <> "/file/download"
    url' :: [NodeId] -> Text
    url' mn' = T.pack $ showBaseUrl $ baseUrl { baseUrlPath = T.unpack $ path mn' }
