{-# LANGUAGE TypeApplications    #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Gargantext.Database.Transactional.Example where

import Control.Lens
import Data.Aeson qualified as JSON
import Gargantext.Core (toDBid)
import Gargantext.Core.Types
import Gargantext.Core.Types.Individu
import Gargantext.Database.Action.Share hiding (getFolderId)
import Gargantext.Database.Admin.Config
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataUser, HyperdataFolder)
import Gargantext.Database.Prelude
import Gargantext.Database.Query.Table.Node (selectNode, selectNodesWith)
import Gargantext.Database.Query.Table.Node.Error
import Gargantext.Database.Query.Table.NodeNode (TargetId(..), SourceId (..), NodeNodePoly (..), nodeNodeTable)
import Gargantext.Database.Query.Table.NodeNode qualified as GGTX hiding (insertNodeNode)
import Gargantext.Database.Query.Tree.Root (selectRoot)
import Gargantext.Database.Schema.Node (_node_id)
import Gargantext.Database.Schema.Node (node_user_id)
import Gargantext.Prelude (panicTrace, headMay)
import Opaleye
import Prelude

-- | In this example we can compose two QUERY operations
-- and the result it's still a pure query.
pureQueryExample :: HasNodeError err
                => NodeId
                -> User
                -> DBCmd err (Node JSON.Value, UserId)
pureQueryExample n u = runDBQuery $ do
  nodeToCheck <- getNode n
  userIdCheck <- getUserId u
  pure (nodeToCheck, userIdCheck)

-- | In this example we can compose a QUERY operation
-- with an UPDATE operation, and the overall \"flavor\"
-- of the DbTx is an UPDATE, so we have to use 'runDBTx'.
simpleTxExample :: HasNodeError err
                => NodeId
                -> DBCmd err Int
simpleTxExample n = runDBTx $ do
  nodeToCheck <- getNode n
  shareNode (SourceId $ _node_id nodeToCheck) (TargetId $ _node_id nodeToCheck)

shareNodeWithTx :: HasNodeError err
                => ShareNodeWith
                -> NodeId
                -- ^ The target node we would like to share, it has
                -- to be a 'NodeFolderShared'.
                -> DBCmd err Int
shareNodeWithTx (ShareNodeWith_User NodeFolderShared u) n = runDBTx $ do
  nodeToCheck <- getNode   n
  userIdCheck <- getUserId u
  if Prelude.not (hasNodeType nodeToCheck NodeTeam)
    then dbFailWith "[G.D.A.S.shareNodeWith] Can share node Team only"
    else
      if (view node_user_id nodeToCheck == userIdCheck)
        then dbFailWith "[G.D.A.S.shareNodeWith] Can share to others only"
        else do
          folderSharedId  <- getFolderId u NodeFolderShared
          shareNode (SourceId folderSharedId) (TargetId n)
shareNodeWithTx _ _ = panicTrace "unimplemented (just testing)"

shareNode :: SourceId -> TargetId -> DBUpdate err Int
shareNode (SourceId sourceId) (TargetId targetId) =
  insertNodeNode [ NodeNode sourceId targetId Nothing Nothing ]

--
-- Mock functions for testing
--

getNode :: forall err x. HasNodeError err => NodeId -> DBQuery err x (Node JSON.Value)
getNode nId = do
  xs <- mkOpaQuery (selectNode (pgNodeId nId))
  case headMay xs of
    Nothing -> dbFail $ _NodeError # (DoesNotExist nId)
    Just  r -> pure r

getUserId :: User -> DBQuery err x UserId
getUserId = undefined

getFolderId :: HasNodeError err => User -> NodeType -> DBQuery err x NodeId
getFolderId u nt = do
  rootId <- getRootId u
  (s :: [ Node HyperdataFolder]) <- mkOpaQuery (selectNodesWith rootId (Just nt) Nothing Nothing)
  case headMay s of
    Nothing -> dbFailWith "[G.D.A.S.getFolderId] No folder shared found"
    Just  f -> pure (_node_id f)

getRootId :: HasNodeError err => User -> DBQuery err x NodeId
getRootId u = do
  (xs :: [ Node HyperdataUser]) <- mkOpaQuery (selectRoot u)
  case headMay xs of
    Nothing -> dbFailWith "[G.D.Q.T.R.getRootId] No root id"
    Just  r -> pure (_node_id r)

insertNodeNode :: [GGTX.NodeNode] -> DBUpdate err Int
insertNodeNode ns = fromIntegral <$> mkOpaInsert (Insert nodeNodeTable ns' rCount (Just doNothing))
  where
    ns' :: [GGTX.NodeNodeWrite]
    ns' = map (\(NodeNode n1 n2 x y)
                -> NodeNode (pgNodeId n1)
                            (pgNodeId n2)
                            (sqlDouble <$> x)
                            (sqlInt4 . toDBid <$> y)
              ) ns
