module IHP.IDE.SchemaDesigner.Controller.Policies where

import IHP.ControllerPrelude
import IHP.IDE.ToolServer.Types

import IHP.IDE.SchemaDesigner.View.Policies.New
import IHP.IDE.SchemaDesigner.View.Policies.Edit

import IHP.IDE.SchemaDesigner.Types
import IHP.IDE.SchemaDesigner.View.Layout (schemaDesignerLayout, findStatementByName, replace, findForeignKey, findTableIndex)
import IHP.IDE.SchemaDesigner.Controller.Helper
import IHP.IDE.SchemaDesigner.Controller.Validation

import qualified Data.Text as Text
import qualified Data.Maybe as Maybe
import qualified Data.List as List

import qualified IHP.IDE.SchemaDesigner.SchemaOperations as SchemaOperations

instance Controller PoliciesController where
    beforeAction :: IO ()
beforeAction = (?context::ControllerContext) =>
((?context::ControllerContext) => Layout) -> IO ()
((?context::ControllerContext) => Layout) -> IO ()
setLayout (?context::ControllerContext) => Layout
Html -> Html
schemaDesignerLayout

    action :: PoliciesController -> IO ()
action NewPolicyAction { Text
$sel:tableName:NewPolicyAction :: PoliciesController -> Text
tableName :: Text
tableName } = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let (Just Statement
table) = Text -> [Statement] -> Maybe Statement
forall (t :: * -> *).
Foldable t =>
Text -> t Statement -> Maybe Statement
findStatementByName Text
tableName [Statement]
statements
        let policy :: Statement
policy = [Statement] -> Statement -> Statement
SchemaOperations.suggestPolicy [Statement]
statements Statement
table
        let columns :: [Column]
columns = Proxy "columns" -> CreateTable -> [Column]
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "columns" (Proxy "columns")
Proxy "columns"
#columns (CreateTable -> [Column]) -> CreateTable -> [Column]
forall a b. (a -> b) -> a -> b
$ Statement -> CreateTable
unsafeGetCreateTable Statement
table
        NewPolicyView -> IO ()
forall view.
(View view, ?context::ControllerContext) =>
view -> IO ()
render NewPolicyView :: [Statement] -> Text -> [Column] -> Statement -> NewPolicyView
NewPolicyView { [Column]
[Statement]
Text
Statement
$sel:policy:NewPolicyView :: Statement
$sel:columns:NewPolicyView :: [Column]
$sel:tableName:NewPolicyView :: Text
$sel:statements:NewPolicyView :: [Statement]
columns :: [Column]
policy :: Statement
statements :: [Statement]
tableName :: Text
.. }

    action EditPolicyAction { Text
tableName :: Text
$sel:tableName:NewPolicyAction :: PoliciesController -> Text
tableName, Text
$sel:policyName:NewPolicyAction :: PoliciesController -> Text
policyName :: Text
policyName } = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let (Just Statement
policy) = [Statement]
statements
                [Statement] -> ([Statement] -> Maybe Statement) -> Maybe Statement
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> (Statement -> Bool) -> [Statement] -> Maybe Statement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find \case
                    CreatePolicy { $sel:name:StatementCreateTable :: Statement -> Text
name = Text
policyName', $sel:tableName:StatementCreateTable :: Statement -> Text
tableName = Text
tableName' } -> Text
policyName' Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
policyName Bool -> Bool -> Bool
&& Text
tableName' Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
tableName
                    Statement
otherwise                                                   -> Bool
False
        let table :: Maybe Statement
table = Text -> [Statement] -> Maybe Statement
forall (t :: * -> *).
Foldable t =>
Text -> t Statement -> Maybe Statement
findStatementByName Text
tableName [Statement]
statements
        let columns :: [Column]
columns = [Column] -> (Statement -> [Column]) -> Maybe Statement -> [Column]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (Proxy "columns" -> CreateTable -> [Column]
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "columns" (Proxy "columns")
Proxy "columns"
#columns (CreateTable -> [Column])
-> (Statement -> CreateTable) -> Statement -> [Column]
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Statement -> CreateTable
unsafeGetCreateTable) Maybe Statement
table

        EditPolicyView -> IO ()
forall view.
(View view, ?context::ControllerContext) =>
view -> IO ()
render EditPolicyView :: [Statement] -> Text -> [Column] -> Statement -> EditPolicyView
EditPolicyView { [Column]
[Statement]
Text
Statement
$sel:policy:EditPolicyView :: Statement
$sel:columns:EditPolicyView :: [Column]
$sel:tableName:EditPolicyView :: Text
$sel:statements:EditPolicyView :: [Statement]
columns :: [Column]
policy :: Statement
statements :: [Statement]
tableName :: Text
.. }

    action PoliciesController
UpdatePolicyAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let tableName :: Text
tableName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableName"
        let name :: Text
name = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"name"

        
        ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema (([Statement] -> [Statement]) -> IO ())
-> ([Statement] -> [Statement]) -> IO ()
forall a b. (a -> b) -> a -> b
$ UpdatePolicyOptions -> [Statement] -> [Statement]
SchemaOperations.updatePolicy UpdatePolicyOptions :: Text
-> Text
-> Text
-> Maybe Expression
-> Maybe Expression
-> UpdatePolicyOptions
SchemaOperations.UpdatePolicyOptions
                { $sel:currentName:UpdatePolicyOptions :: Text
currentName = Text
name
                , $sel:tableName:UpdatePolicyOptions :: Text
tableName = Text
tableName
                , $sel:name:UpdatePolicyOptions :: Text
name = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"policyName"
                , $sel:using:UpdatePolicyOptions :: Maybe Expression
using = ByteString -> Maybe Expression
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"using"
                , $sel:check:UpdatePolicyOptions :: Maybe Expression
check = ByteString -> Maybe Expression
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"check"
                }

        TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowTableAction :: Text -> TablesController
ShowTableAction { Text
$sel:tableName:TablesAction :: Text
tableName :: Text
.. }
    
    action PoliciesController
CreatePolicyAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let tableName :: Text
tableName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"tableName"
        
        let addPolicy :: [Statement] -> [Statement]
addPolicy = AddPolicyOptions -> [Statement] -> [Statement]
SchemaOperations.addPolicy AddPolicyOptions :: Text
-> Text -> Maybe Expression -> Maybe Expression -> AddPolicyOptions
SchemaOperations.AddPolicyOptions
                { $sel:tableName:AddPolicyOptions :: Text
tableName = Text
tableName
                , $sel:name:AddPolicyOptions :: Text
name = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"policyName"
                , $sel:using:AddPolicyOptions :: Maybe Expression
using = ByteString -> Maybe Expression
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"using"
                , $sel:check:AddPolicyOptions :: Maybe Expression
check = ByteString -> Maybe Expression
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"check"
                }
        let enableRLS :: [Statement] -> [Statement]
enableRLS = Text -> [Statement] -> [Statement]
SchemaOperations.enableRowLevelSecurity Text
tableName

        ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema ([Statement] -> [Statement]
enableRLS ([Statement] -> [Statement])
-> ([Statement] -> [Statement]) -> [Statement] -> [Statement]
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [Statement] -> [Statement]
addPolicy)

        TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowTableAction :: Text -> TablesController
ShowTableAction { Text
tableName :: Text
$sel:tableName:TablesAction :: Text
.. }

    action DeletePolicyAction { Text
tableName :: Text
$sel:tableName:NewPolicyAction :: PoliciesController -> Text
tableName, Text
policyName :: Text
$sel:policyName:NewPolicyAction :: PoliciesController -> Text
policyName } = do
        let deletePolicy :: [Statement] -> [Statement]
deletePolicy = DeletePolicyOptions -> [Statement] -> [Statement]
SchemaOperations.deletePolicy DeletePolicyOptions :: Text -> Text -> DeletePolicyOptions
SchemaOperations.DeletePolicyOptions { Text
$sel:tableName:DeletePolicyOptions :: Text
tableName :: Text
tableName, Text
$sel:policyName:DeletePolicyOptions :: Text
policyName :: Text
policyName }
        let disableRLSIfNoPolicies :: [Statement] -> [Statement]
disableRLSIfNoPolicies = Text -> [Statement] -> [Statement]
SchemaOperations.disableRowLevelSecurityIfNoPolicies Text
tableName
        ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema ([Statement] -> [Statement]
disableRLSIfNoPolicies ([Statement] -> [Statement])
-> ([Statement] -> [Statement]) -> [Statement] -> [Statement]
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [Statement] -> [Statement]
deletePolicy)

        TablesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowTableAction :: Text -> TablesController
ShowTableAction { Text
tableName :: Text
$sel:tableName:TablesAction :: Text
.. }