module Database.GP.SqlGenerator
  ( insertStmtFor,
    updateStmtFor,
    selectStmtFor,
    deleteStmtFor,
    selectAllStmtFor,
    selectAllWhereStmtFor,
    createTableStmtFor,
    dropTableStmtFor,
  )
where

import           Data.List (intercalate)
import           Database.GP.Entity
import           Database.GP.TypeInfo

-- | A function that returns an SQL insert statement for an entity. Type 'a' must be an instance of Data.
-- The function will use the field names of the data type to generate the column names in the insert statement.
-- The values of the fields will be used as the values in the insert statement.
-- Output example: INSERT INTO Person (id, name, age, address) VALUES (123456, "Alice", 25, "123 Main St");
insertStmtFor :: Entity a => a -> String
insertStmtFor :: forall a. Entity a => a -> String
insertStmtFor a
x =
  String
"INSERT INTO "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
" ("
    forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
columns
    forall a. [a] -> [a] -> [a]
++ String
") VALUES ("
    forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
", " (Int -> [String]
params (forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
columns))
    forall a. [a] -> [a] -> [a]
++ String
");"
  where
    columns :: [String]
columns = forall a. Entity a => a -> [String]
columnNamesFor a
x


columnNamesFor :: Entity a => a -> [String]
columnNamesFor :: forall a. Entity a => a -> [String]
columnNamesFor a
x =  forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd [(String, String)]
fieldColumnPairs
  where
    fieldColumnPairs :: [(String, String)]
fieldColumnPairs = forall a. Entity a => a -> [(String, String)]
fieldsToColumns a
x 


params :: Int -> [String]
params :: Int -> [String]
params Int
n = forall a. Int -> a -> [a]
replicate Int
n String
"?"

-- | A function that returns an SQL update statement for an entity. Type 'a' must be an instance of Entity.
updateStmtFor :: Entity a => a -> String
updateStmtFor :: forall a. Entity a => a -> String
updateStmtFor a
x =
  String
"UPDATE "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
" SET "
    forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
updatePairs
    forall a. [a] -> [a] -> [a]
++ String
" WHERE "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
idColumn a
x
    forall a. [a] -> [a] -> [a]
++ String
" = ?"
    forall a. [a] -> [a] -> [a]
++ String
";"
  where
    updatePairs :: [String]
updatePairs = forall a b. (a -> b) -> [a] -> [b]
map (forall a. [a] -> [a] -> [a]
++ String
" = ?") (forall a. Entity a => a -> [String]
columnNamesFor a
x)

idColumn :: Entity a => a -> String
idColumn :: forall a. Entity a => a -> String
idColumn a
x = forall a. Entity a => a -> String -> String
columnNameFor a
x (forall a. Entity a => a -> String
idField a
x)

-- | A function that returns an SQL select statement for entity type `a` with primary key `id`.
selectStmtFor :: forall a. (Entity a) => TypeInfo a -> String
selectStmtFor :: forall a. Entity a => TypeInfo a -> String
selectStmtFor TypeInfo a
ti =
  String
"SELECT "
    forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
", " (forall a. Entity a => a -> [String]
columnNamesFor a
x)
    forall a. [a] -> [a] -> [a]
++ String
" FROM "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
" WHERE "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
idColumn a
x
    forall a. [a] -> [a] -> [a]
++ String
" = ?;"
  where
    x :: a
x = forall a. Entity a => TypeInfo a -> a
evidenceFrom TypeInfo a
ti :: a

selectAllStmtFor :: forall a. (Entity a) => TypeInfo a -> String
selectAllStmtFor :: forall a. Entity a => TypeInfo a -> String
selectAllStmtFor TypeInfo a
ti =
  String
"SELECT "
    forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
", " (forall a. Entity a => a -> [String]
columnNamesFor a
x)
    forall a. [a] -> [a] -> [a]
++ String
" FROM "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
";"
  where
    x :: a
x = forall a. Entity a => TypeInfo a -> a
evidenceFrom TypeInfo a
ti :: a

selectAllWhereStmtFor :: forall a. (Entity a) => TypeInfo a -> String -> String
selectAllWhereStmtFor :: forall a. Entity a => TypeInfo a -> String -> String
selectAllWhereStmtFor TypeInfo a
ti String
field =
  String
"SELECT "
    forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
", " (forall a. Entity a => a -> [String]
columnNamesFor a
x)
    forall a. [a] -> [a] -> [a]
++ String
" FROM "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
" WHERE "
    forall a. [a] -> [a] -> [a]
++ String
column
    forall a. [a] -> [a] -> [a]
++ String
" = ?;"
  where
    x :: a
x = forall a. Entity a => TypeInfo a -> a
evidenceFrom TypeInfo a
ti :: a
    column :: String
column = forall a. Entity a => a -> String -> String
columnNameFor a
x String
field

deleteStmtFor :: Entity a => a -> String
deleteStmtFor :: forall a. Entity a => a -> String
deleteStmtFor a
x =
  String
"DELETE FROM "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
" WHERE "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
idColumn a
x
    forall a. [a] -> [a] -> [a]
++ String
" = ?;"

createTableStmtFor :: forall a. (Entity a) => TypeInfo a -> String
createTableStmtFor :: forall a. Entity a => TypeInfo a -> String
createTableStmtFor TypeInfo a
ti =
  String
"CREATE TABLE "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
" ("
    forall a. [a] -> [a] -> [a]
++ forall a. [a] -> [[a]] -> [a]
intercalate String
", " (forall a b. (a -> b) -> [a] -> [b]
map (\(String
f,String
c) -> String
c forall a. [a] -> [a] -> [a]
++ String
" " forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String -> String
columnTypeFor a
x String
f forall a. [a] -> [a] -> [a]
++ String -> String
optionalPK String
f) (forall a. Entity a => a -> [(String, String)]
fieldsToColumns a
x))
    forall a. [a] -> [a] -> [a]
++ String
");"
  where
    x :: a
x = forall a. Entity a => TypeInfo a -> a
evidenceFrom TypeInfo a
ti :: a
    isIdField :: String -> Bool
isIdField String
f = String
f forall a. Eq a => a -> a -> Bool
== forall a. Entity a => a -> String
idField a
x
    optionalPK :: String -> String
optionalPK String
f = if String -> Bool
isIdField String
f then String
" PRIMARY KEY" else String
""
    
    
columnTypeFor :: forall a. (Entity a) => a -> String -> String
columnTypeFor :: forall a. Entity a => a -> String -> String
columnTypeFor a
x String
field = 
  case String
fType of
    String
"Int" -> String
"INTEGER"
    String
"String" -> String
"TEXT"
    String
"Double" -> String
"REAL"
    String
"Float" -> String
"REAL"
    String
"Bool" -> String
"INT"
    String
_ -> String
"TEXT"
    where
      maybeFType :: Maybe TypeRep
maybeFType = forall a. Entity a => a -> String -> Maybe TypeRep
maybeFieldTypeFor a
x String
field
      fType :: String
fType = forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"OTHER" forall a. Show a => a -> String
show Maybe TypeRep
maybeFType


dropTableStmtFor :: forall a. (Entity a) => TypeInfo a -> String
dropTableStmtFor :: forall a. Entity a => TypeInfo a -> String
dropTableStmtFor TypeInfo a
ti =
  String
"DROP TABLE IF EXISTS "
    forall a. [a] -> [a] -> [a]
++ forall a. Entity a => a -> String
tableName a
x
    forall a. [a] -> [a] -> [a]
++ String
";"
  where
    x :: a
x = forall a. Entity a => TypeInfo a -> a
evidenceFrom TypeInfo a
ti :: a