module IHP.IDE.SchemaDesigner.View.Columns.Edit where

import IHP.ViewPrelude
import IHP.IDE.SchemaDesigner.Types
import qualified IHP.IDE.SchemaDesigner.Compiler as Compiler
import IHP.IDE.ToolServer.Types
import IHP.IDE.SchemaDesigner.View.Layout

data EditColumnView = EditColumnView
    { EditColumnView -> [Statement]
statements :: [Statement]
    , EditColumnView -> Text
tableName :: Text
    , EditColumnView -> Int
columnId :: Int
    , EditColumnView -> Column
column :: Column
    , EditColumnView -> [Text]
enumNames :: [Text]
    }

instance View EditColumnView where
    html :: (?context::ControllerContext, ?view::EditColumnView) =>
EditColumnView -> Html
html EditColumnView { $sel:column:EditColumnView :: EditColumnView -> Column
column = column :: Column
column@Column { Text
name :: Text
$sel:name:Column :: Column -> Text
name }, Int
[Text]
[Statement]
Text
$sel:statements:EditColumnView :: EditColumnView -> [Statement]
$sel:tableName:EditColumnView :: EditColumnView -> Text
$sel:columnId:EditColumnView :: EditColumnView -> Int
$sel:enumNames:EditColumnView :: EditColumnView -> [Text]
statements :: [Statement]
tableName :: Text
columnId :: Int
enumNames :: [Text]
.. } = [hsx|
        <div class="row no-gutters bg-white" id="schema-designer-viewer">
            {renderObjectSelector (zip [0..] statements) (Just tableName)}
            {renderColumnSelector tableName (zip [0..] columns) statements}
        </div>
        {migrationStatus}
        {renderModal modal}
    |]
        where
            table :: Maybe Statement
table = Text -> [Statement] -> Maybe Statement
forall {t :: * -> *}.
Foldable t =>
Text -> t Statement -> Maybe Statement
findStatementByName Text
tableName [Statement]
statements
            columns :: [Column]
columns = [Column] -> (Statement -> [Column]) -> Maybe Statement -> [Column]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] ((.columns) (CreateTable -> [Column])
-> (Statement -> CreateTable) -> Statement -> [Column]
forall b c a. (b -> c) -> (a -> b) -> a -> c
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
            primaryKeyColumns :: [Text]
primaryKeyColumns = [Text] -> (Statement -> [Text]) -> Maybe Statement -> [Text]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (PrimaryKeyConstraint -> [Text]
primaryKeyColumnNames (PrimaryKeyConstraint -> [Text])
-> (Statement -> PrimaryKeyConstraint) -> Statement -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (.primaryKeyConstraint) (CreateTable -> PrimaryKeyConstraint)
-> (Statement -> CreateTable) -> Statement -> PrimaryKeyConstraint
forall b c a. (b -> c) -> (a -> b) -> a -> c
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

            isArrayType :: PostgresType -> Bool
isArrayType (PArray PostgresType
_) = Bool
True
            isArrayType PostgresType
_ = Bool
False

            isPrimaryKey :: Bool
            isPrimaryKey :: Bool
isPrimaryKey = Text
name Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
primaryKeyColumns

            modalContent :: Html
modalContent = [hsx|
                <form method="POST" action={UpdateColumnAction}>
                    <input type="hidden" name="tableName" value={tableName}/>
                    <input type="hidden" name="columnId" value={tshow columnId}/>

                    <div class="form-group">
                        <input
                            id="nameInput"
                            name="name"
                            type="text"
                            class="form-control"
                            autofocus="autofocus"
                            value={column.name}
                            data-table-name-singular={singularize tableName}
                            />
                    </div>

                    <div class="form-group">
                        {typeSelector (Just (column.columnType)) enumNames}

                        <div class="d-flex text-muted mt-1" id="column-options">
                            <div class="custom-control custom-checkbox mr-2">
                                <input id="allowNull" type="checkbox" name="allowNull" class="custom-control-input" checked={not (column.notNull)}/>
                                <label class="mr-1 custom-control-label" for="allowNull">
                                    Nullable
                                </label>
                            </div>

                            <div class="custom-control custom-checkbox mr-2">
                                <input type="checkbox" id="isUnique" name="isUnique" class="custom-control-input" checked={column.isUnique}/>
                                <label class="custom-control-label" for="isUnique">
                                    Unique
                                </label>
                            </div>

                            <div class="custom-control custom-checkbox mr-2">
                                <input type="checkbox" id="primaryKey" name="primaryKey" class="custom-control-input" checked={isPrimaryKey}/>
                                <label class="custom-control-label" for="primaryKey">
                                    Primary Key
                                </label>
                            </div>

                            <div class="custom-control custom-checkbox mr-2">
                                <input id="isArray" type="checkbox" name="isArray" class="custom-control-input" checked={isArrayType (column.columnType)}/>
                                <label class="custom-control-label">
                                     Array Type
                                </label>
                            </div>
                        </div>
                    </div>

                    <div class="form-group row">
                        {defaultSelector (column.defaultValue)}
                    </div>

                    <div class="text-right">
                        <button type="submit" class="btn btn-primary">Edit Column</button>
                    </div>
                    <input type="hidden" name="primaryKey" value={inputValue False}/>
                    <input type="hidden" name="allowNull" value={inputValue False}/>
                    <input type="hidden" name="isUnique" value={inputValue False}/>
                    <input type="hidden" name="isArray" value={inputValue False}/>
                </form>
            |]
            modalFooter :: Maybe Html
modalFooter = Maybe Html
forall a. Monoid a => a
mempty
            modalCloseUrl :: Text
modalCloseUrl = TablesController -> Text
forall controller. HasPath controller => controller -> Text
pathTo ShowTableAction { Text
tableName :: Text
$sel:tableName:TablesAction :: Text
tableName }
            modalTitle :: Text
modalTitle = Text
"Edit Column"
            modal :: Modal
modal = Modal { Html
modalContent :: Html
$sel:modalContent:Modal :: Html
modalContent, Maybe Html
modalFooter :: Maybe Html
$sel:modalFooter:Modal :: Maybe Html
modalFooter, Text
modalCloseUrl :: Text
$sel:modalCloseUrl:Modal :: Text
modalCloseUrl, Text
modalTitle :: Text
$sel:modalTitle:Modal :: Text
modalTitle }

typeSelector :: Maybe PostgresType -> [Text] -> Html
typeSelector :: Maybe PostgresType -> [Text] -> Html
typeSelector Maybe PostgresType
postgresType [Text]
enumNames = [hsx|
        <select id="typeSelector" name="columnType" class="form-control select2-simple">
            <optgroup label="Common Types">
                {option isSelected "TEXT" "Text"}
                {option isSelected "INT" "Int"}
                {option isSelected "UUID" "UUID"}
                {option isSelected "BOOLEAN" "Bool"}
                {option isSelected "DATE" "Date / Day"}
                {option isSelected "TIMESTAMP WITH TIME ZONE" "Timestamp (UTCTime)"}
                {option isSelected "SERIAL" "Serial"}
            </optgroup>
            {customenums enumNames}
            <optgroup label="Other Types">
                {option isSelected "TIMESTAMP WITHOUT TIME ZONE" "Timestamp (LocalTime)"}
                {option isSelected "REAL" "Float"}
                {option isSelected "DOUBLE PRECISION" "Double"}
                {option isSelected "POINT" "Point"}
                {option isSelected "BYTEA" "Binary"}
                {option isSelected "Time" "Time"}
                {option isSelected "INTERVAL" "Interval"}
                {option isSelected "BIGSERIAL" "Bigserial"}
                {option isSelected "SMALLINT" "Int (16bit)"}
                {option isSelected "BIGINT" "Int (64bit)"}
                {option isSelected "JSONB" "JSON"}
                {option isSelected "INET" "IP Address"}
                {option isSelected "TSVECTOR" "TSVector"}
            </optgroup>
        </select>
|]
    where
        isSelected :: Maybe Text
        isSelected :: Maybe Text
isSelected = (PostgresType -> Text) -> Maybe PostgresType -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap PostgresType -> Text
Compiler.compilePostgresType Maybe PostgresType
postgresType

        renderEnumType :: Text -> Html
renderEnumType Text
enum = Maybe Text -> Text -> Text -> Html
option Maybe Text
isSelected Text
enum Text
enum
        option :: Maybe Text -> Text -> Text -> Html
        option :: Maybe Text -> Text -> Text -> Html
option Maybe Text
selected Text
value Text
text = case Maybe Text
selected of
            Maybe Text
Nothing -> [hsx|<option value={value}>{text}</option>|]
            Just Text
selection ->
                if Text
selection Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
value Bool -> Bool -> Bool
|| Text
selection Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
value Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"[]"
                    then [hsx|<option value={value} selected="selected">{text}</option>|]
                    else [hsx|<option value={value}>{text}</option>|]
        customenums :: [Text] -> Html
customenums [] = [hsx| |]
        customenums [Text]
xs = [hsx| <optgroup label="Custom Enums">
                                {forEach xs renderEnumType}
                               </optgroup>
                         |]

defaultSelector :: Maybe Expression -> Html
defaultSelector :: Maybe Expression -> Html
defaultSelector Maybe Expression
defValue = [hsx|
    <div class="col-sm-10">
        <select id="defaultSelector" name="defaultValue" class="form-control select2">
            {forEach values renderValue}
        </select>
    </div>
|]
    where
        suggestedValues :: [Maybe Expression]
suggestedValues = [Maybe Expression
forall a. Maybe a
Nothing, Expression -> Maybe Expression
forall a. a -> Maybe a
Just (Text -> Expression
TextExpression Text
""), Expression -> Maybe Expression
forall a. a -> Maybe a
Just (Text -> Expression
VarExpression Text
"NULL"), Expression -> Maybe Expression
forall a. a -> Maybe a
Just (Text -> [Expression] -> Expression
CallExpression Text
"NOW" [])]
        values :: [Maybe Expression]
values = if Maybe Expression
defValue Maybe Expression -> [Maybe Expression] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Maybe Expression]
suggestedValues then [Maybe Expression]
suggestedValues else Maybe Expression
defValueMaybe Expression -> [Maybe Expression] -> [Maybe Expression]
forall a. a -> [a] -> [a]
:[Maybe Expression]
suggestedValues

        renderValue :: Maybe Expression -> Html
        renderValue :: Maybe Expression -> Html
renderValue e :: Maybe Expression
e@(Just Expression
expression) = [hsx|<option value={Compiler.compileExpression expression} selected={e == defValue}>{displayedValue}</option>|]
            where
                displayedValue :: Text
displayedValue = case Expression
expression of
                    TextExpression Text
"" -> Text
"\"\""
                    Expression
_ -> Expression -> Text
Compiler.compileExpression Expression
expression
        renderValue Maybe Expression
Nothing = [hsx|<option value="" selected={Nothing == defValue}>No default</option>|]