module IHP.IDE.Data.View.Layout
    ( customQuery
    , tableHead
    , renderRows
    , sqlValueToText
    , renderId
    , isBoolField
    , isSqlFunction
    , isSqlFunction_
    , fillField
    , getColDefaultValue
    , renderRowValue
    , renderDefaultWithoutType
    , isBooleanParam
    , headerNav
    ) where

import IHP.ViewPrelude
import IHP.IDE.ToolServer.Types
import IHP.IDE.ToolServer.Routes ()
import qualified Data.Text as Text
import IHP.IDE.ToolServer.Helper.View

customQuery :: Text -> Html
customQuery :: Text -> Html
customQuery Text
input = [hsx|<div class="p-2 rounded mt-2" style="background-color: #002B36;"><div id="queryInput" style="height:16px">{input}</div></div>|]

tableHead :: [[DynamicField]] -> Text -> Html
tableHead :: [[DynamicField]] -> Text -> Html
tableHead [[DynamicField]]
rows Text
tableName =
    [hsx|
        <thead>
            <tr>
                {forEach (columnNames rows) renderColumnHead}
                <th>
                    <div class="d-flex">
                        <a
                            href={NewRowAction tableName}
                            class="btn btn-link btn-add"
                            data-toggle="tooltip"
                            data-placement="bottom"
                            title={"Add " <> tableNameToModelName tableName}
                        >{addIcon}</a>
                    </div>
                </th>
            </tr>
        </thead>
    |]
    where
        columnNames :: [[a]] -> [b]
columnNames [[a]]
rows = (a -> b) -> [a] -> [b]
forall a b. (a -> b) -> [a] -> [b]
map (.fieldName) ([a] -> Maybe [a] -> [a]
forall a. a -> Maybe a -> a
fromMaybe [] ([[a]] -> Maybe [a]
forall a. [a] -> Maybe a
head [[a]]
rows))
        renderColumnHead :: a -> Html
renderColumnHead a
name = [hsx|<th>{name}</th>|]

renderRows :: [[DynamicField]] -> Html -> Text -> Html
renderRows :: [[DynamicField]] -> Html -> Text -> Html
renderRows [[DynamicField]]
rows Html
body Text
tableName = [hsx|
    <table class="table table-sm table-hover data-rows-table">
        {tableHead rows tableName}
        {body}
    </table>
|]

sqlValueToText :: Maybe ByteString -> Text
sqlValueToText :: Maybe ByteString -> Text
sqlValueToText (Just ByteString
value) = ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
value
sqlValueToText Maybe ByteString
Nothing = Text
"NULL"

renderId :: a -> [Char]
renderId a
id = Int -> [Char] -> [Char]
forall a. Int -> [a] -> [a]
take Int
4 (a -> [Char]
forall a b. ConvertibleStrings a b => a -> b
cs a
id) [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
".." [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char] -> [Char]
forall a. [a] -> [a]
reverse (Int -> [Char] -> [Char]
forall a. Int -> [a] -> [a]
take Int
4 ([Char] -> [Char]
forall a. [a] -> [a]
reverse (a -> [Char]
forall a b. ConvertibleStrings a b => a -> b
cs a
id)))

isBoolField :: a -> t r -> Bool
isBoolField a
fieldName t r
tableCols = case ((r -> Bool) -> t r -> Maybe r
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\r
c -> r
c.columnName a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== (a -> a
forall a b. ConvertibleStrings a b => a -> b
cs a
fieldName)) t r
tableCols) of
    Just r
columnDef -> (r
columnDef.columnType) a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
"boolean"
    Maybe r
Nothing -> Bool
False

isSqlFunction :: Text -> Bool
isSqlFunction :: Text -> Bool
isSqlFunction Text
text = Text
text Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem`
    [ Text
"uuid_generate_v4()"
    , Text
"NOW()"
    , Text
"NULL"]

isSqlFunction_ :: ByteString -> Bool
isSqlFunction_ :: ByteString -> Bool
isSqlFunction_ ByteString
text = ByteString
text ByteString -> [ByteString] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem`
    [ ByteString
"uuid_generate_v4()"
    , ByteString
"NOW()"
    , ByteString
"NULL"]

fillField :: r -> a -> a -> a
fillField r
col a
value a
isBoolField = a
"fillField('" a -> a -> a
forall a. Semigroup a => a -> a -> a
<> r
col.columnName a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
"', '" a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
value a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
"'," a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
isBoolField a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
");"

getColDefaultValue :: ColumnDefinition -> Text
getColDefaultValue :: ColumnDefinition -> Text
getColDefaultValue ColumnDefinition { Maybe Text
columnDefault :: Maybe Text
$sel:columnDefault:ColumnDefinition :: ColumnDefinition -> Maybe Text
columnDefault, Bool
isNullable :: Bool
$sel:isNullable:ColumnDefinition :: ColumnDefinition -> Bool
isNullable } = case Maybe Text
columnDefault of
        Just Text
value -> Text
value
        Maybe Text
Nothing -> if Bool
isNullable
            then Text
"NULL"
            else Text
""

renderRowValue :: Maybe ByteString -> Text
renderRowValue :: Maybe ByteString -> Text
renderRowValue (Just ByteString
value) = Text
"'" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
value Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"'"
renderRowValue Maybe ByteString
Nothing = Text
"NULL"

renderDefaultWithoutType :: Text -> Text
renderDefaultWithoutType :: Text -> Text
renderDefaultWithoutType Text
"" = Text
""
renderDefaultWithoutType Text
input = case [Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
Text.splitOn Text
"'" Text
input) of
        Int
3 -> (HasCallStack => Text -> Text -> [Text]
Text -> Text -> [Text]
Text.splitOn Text
"'" Text
input) [Text] -> Int -> Text
forall a. HasCallStack => [a] -> Int -> a
!! Int
1
        Int
_ -> Text
input

isBooleanParam :: Bool -> ColumnDefinition -> Html
isBooleanParam :: Bool -> ColumnDefinition -> Html
isBooleanParam Bool
isBool ColumnDefinition
def = [hsx|
<input
    type="hidden"
    name={def.columnName <> "-isBoolean"}
    value={inputValue isBool}
    />
|]


headerNav :: Html
headerNav :: Html
headerNav = [hsx|
    <div class="view-selector">
        <div class="container-fluid">
            <a href={ShowDatabaseAction} class={classes [("active", databaseActive)]}>
                Database
            </a>

            <a href={NewQueryAction} class={classes [("active", sqlActive)]}>
                SQL
            </a>
        </div>
    </div>
|]
    where
        databaseActive :: Bool
        databaseActive :: Bool
databaseActive = forall controller.
(?context::ControllerContext, Typeable controller) =>
Bool
forall {k} (controller :: k).
(?context::ControllerContext, Typeable controller) =>
Bool
isActiveController @DataController Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
sqlActive

        sqlActive :: Bool
        sqlActive :: Bool
sqlActive = DataController -> Bool
forall controller.
(?context::ControllerContext, PathString controller) =>
controller -> Bool
isActivePath DataController
NewQueryAction Bool -> Bool -> Bool
|| DataController -> Bool
forall controller.
(?context::ControllerContext, PathString controller) =>
controller -> Bool
isActivePath DataController
QueryAction