module IHP.IDE.SchemaDesigner.Controller.EnumValues where

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

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

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

import qualified IHP.IDE.SchemaDesigner.SchemaOperations as SchemaOperations

instance Controller EnumValuesController where
    beforeAction :: (?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::EnumValuesController) =>
IO ()
beforeAction = (?context::ControllerContext) =>
((?context::ControllerContext) => Layout) -> IO ()
((?context::ControllerContext) => Layout) -> IO ()
setLayout (?context::ControllerContext) => Layout
Layout
Html -> Html
schemaDesignerLayout

    action :: (?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::EnumValuesController) =>
EnumValuesController -> IO ()
action NewEnumValueAction { Text
enumName :: Text
$sel:enumName:NewEnumValueAction :: EnumValuesController -> Text
enumName } = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        NewEnumValueView -> IO ()
forall view.
(View view, ?context::ControllerContext) =>
view -> IO ()
render NewEnumValueView { [Statement]
Text
enumName :: Text
statements :: [Statement]
$sel:statements:NewEnumValueView :: [Statement]
$sel:enumName:NewEnumValueView :: Text
.. }

    action EnumValuesController
CreateEnumValueAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let enumName :: Text
enumName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"enumName"
        let enumValueName :: Text
enumValueName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"enumValueName"
        let validationResult :: ValidatorResult
validationResult = Text
enumValueName Text -> (Text -> ValidatorResult) -> ValidatorResult
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> [Statement] -> Maybe Text -> Text -> ValidatorResult
validateEnumValue [Statement]
statements Maybe Text
forall a. Maybe a
Nothing
        case ValidatorResult
validationResult of
            Failure Text
message ->
                (?context::ControllerContext) => Text -> IO ()
Text -> IO ()
setErrorMessage Text
message
            ValidatorResult
Success -> do
                ([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
$ Text -> Text -> [Statement] -> [Statement]
SchemaOperations.addValueToEnum Text
enumName Text
enumValueName

        -- The form to save an enum has two save buttons:
        --
        -- 1. Save
        -- 2. Save & Add another
        --
        case forall a.
(?context::ControllerContext, ParamReader a) =>
a -> ByteString -> a
paramOrDefault @Text Text
"Save" ByteString
"submit" of
            Text
"Save" -> EnumsController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowEnumAction { Text
enumName :: Text
$sel:enumName:ShowEnumAction :: Text
.. }
            Text
"Save & Add Another" -> EnumValuesController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo NewEnumValueAction { Text
$sel:enumName:NewEnumValueAction :: Text
enumName :: Text
.. }

    action EditEnumValueAction { Int
Text
$sel:enumName:NewEnumValueAction :: EnumValuesController -> Text
enumName :: Text
valueId :: Int
$sel:valueId:NewEnumValueAction :: EnumValuesController -> Int
.. } = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let valueId :: Int
valueId = ByteString -> Int
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"valueId"
        let enumName :: Text
enumName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"enumName"
        let enum :: Maybe Statement
enum = Text -> [Statement] -> Maybe Statement
forall {t :: * -> *}.
Foldable t =>
Text -> t Statement -> Maybe Statement
findStatementByName Text
enumName [Statement]
statements
        let values :: [Text]
values = [Text] -> (Statement -> [Text]) -> Maybe Statement -> [Text]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (.values) Maybe Statement
enum
        let value :: Text
value = (Text -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ([Text]
values [Text] -> Int -> Text
forall a. HasCallStack => [a] -> Int -> a
!! Int
valueId))
        EditEnumValueView -> IO ()
forall view.
(View view, ?context::ControllerContext) =>
view -> IO ()
render EditEnumValueView { Int
[Statement]
Text
statements :: [Statement]
valueId :: Int
enumName :: Text
value :: Text
$sel:statements:EditEnumValueView :: [Statement]
$sel:enumName:EditEnumValueView :: Text
$sel:valueId:EditEnumValueView :: Int
$sel:value:EditEnumValueView :: Text
.. }

    action EnumValuesController
UpdateEnumValueAction = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let enumName :: Text
enumName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"enumName"
        let valueId :: Int
valueId = ByteString -> Int
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"valueId"
        let newValue :: Text
newValue = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"enumValueName" :: Text
        let enum :: Maybe Statement
enum = Text -> [Statement] -> Maybe Statement
forall {t :: * -> *}.
Foldable t =>
Text -> t Statement -> Maybe Statement
findStatementByName Text
enumName [Statement]
statements
        let values :: [Text]
values = [Text] -> (Statement -> [Text]) -> Maybe Statement -> [Text]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (.values) Maybe Statement
enum
        let value :: Text
value = [Text]
values [Text] -> Int -> Text
forall a. HasCallStack => [a] -> Int -> a
!! Int
valueId
        let validationResult :: ValidatorResult
validationResult = Text
newValue Text -> (Text -> ValidatorResult) -> ValidatorResult
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> [Statement] -> Maybe Text -> Text -> ValidatorResult
validateEnumValue [Statement]
statements (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
value)
        case ValidatorResult
validationResult of
            Failure Text
message ->
                (?context::ControllerContext) => Text -> IO ()
Text -> IO ()
setErrorMessage Text
message
            ValidatorResult
Success ->
                ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema ((Statement -> Statement) -> [Statement] -> [Statement]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text -> Int -> Statement -> Statement
updateValueInEnum Text
enumName Text
newValue Int
valueId))
        EnumsController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowEnumAction { Text
$sel:enumName:ShowEnumAction :: Text
enumName :: Text
.. }

    action DeleteEnumValueAction { Int
Text
$sel:enumName:NewEnumValueAction :: EnumValuesController -> Text
$sel:valueId:NewEnumValueAction :: EnumValuesController -> Int
enumName :: Text
valueId :: Int
.. } = do
        [Statement]
statements <- IO [Statement]
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
IO [Statement]
readSchema
        let enumName :: Text
enumName = ByteString -> Text
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"enumName"
        let valueId :: Int
valueId = ByteString -> Int
forall valueType.
(?context::ControllerContext, ParamReader valueType) =>
ByteString -> valueType
param ByteString
"valueId"
        ([Statement] -> [Statement]) -> IO ()
forall controller.
(?context::ControllerContext, ?modelContext::ModelContext,
 ?theAction::controller) =>
([Statement] -> [Statement]) -> IO ()
updateSchema ((Statement -> Statement) -> [Statement] -> [Statement]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> Int -> Statement -> Statement
deleteValueInEnum Text
enumName Int
valueId))
        EnumsController -> IO ()
forall action.
(?context::ControllerContext, HasPath action) =>
action -> IO ()
redirectTo ShowEnumAction { Text
$sel:enumName:ShowEnumAction :: Text
enumName :: Text
.. }

updateValueInEnum :: Text -> Text -> Int -> Statement -> Statement
updateValueInEnum :: Text -> Text -> Int -> Statement -> Statement
updateValueInEnum Text
enumName Text
value Int
valueId (table :: Statement
table@CreateEnumType { Text
name :: Text
$sel:name:StatementCreateTable :: Statement -> Text
name, [Text]
values :: [Text]
$sel:values:StatementCreateTable :: Statement -> [Text]
values }) | Text
name Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
enumName =
    Statement
table { $sel:values:StatementCreateTable :: [Text]
values = (Int -> Text -> [Text] -> [Text]
forall a. Int -> a -> [a] -> [a]
replace Int
valueId Text
value [Text]
values) }
updateValueInEnum Text
enumName Text
value Int
valueId Statement
statement = Statement
statement

deleteValueInEnum :: Text -> Int -> Statement -> Statement
deleteValueInEnum :: Text -> Int -> Statement -> Statement
deleteValueInEnum Text
enumName Int
valueId (table :: Statement
table@CreateEnumType { Text
$sel:name:StatementCreateTable :: Statement -> Text
name :: Text
name, [Text]
$sel:values:StatementCreateTable :: Statement -> [Text]
values :: [Text]
values }) | Text
name Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
enumName =
    Statement
table { $sel:values:StatementCreateTable :: [Text]
values = Text -> [Text] -> [Text]
forall a. Eq a => a -> [a] -> [a]
delete ([Text]
values [Text] -> Int -> Text
forall a. HasCallStack => [a] -> Int -> a
!! Int
valueId) [Text]
values}
deleteValueInEnum Text
enumName Int
valueId Statement
statement = Statement
statement

validateEnumValue :: [Statement] -> Maybe Text -> Validator Text
validateEnumValue :: [Statement] -> Maybe Text -> Text -> ValidatorResult
validateEnumValue [Statement]
statements = Text -> [Text] -> Maybe Text -> Text -> ValidatorResult
validateNameInSchema Text
"enum value" ([Statement] -> [Text]
getAllObjectNames [Statement]
statements)

getAllEnumValues :: [Statement] -> [Text]
getAllEnumValues :: [Statement] -> [Text]
getAllEnumValues [Statement]
statements = [[Text]] -> [Text]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Text]] -> [Text]) -> [[Text]] -> [Text]
forall a b. (a -> b) -> a -> b
$ (Statement -> Maybe [Text]) -> [Statement] -> [[Text]]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Statement -> Maybe [Text]
extractEnumValues [Statement]
statements
    where
        extractEnumValues :: Statement -> Maybe [Text]
extractEnumValues CreateEnumType { [Text]
$sel:values:StatementCreateTable :: Statement -> [Text]
values :: [Text]
values } = [Text] -> Maybe [Text]
forall a. a -> Maybe a
Just [Text]
values
        extractEnumValues Statement
_ = Maybe [Text]
forall a. Maybe a
Nothing