content: {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE OverloadedLabels #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE TypeApplications #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE GADTs #-}
  {-# LANGUAGE DeriveGeneric #-}
  
  module Gauge.Schema where
  
  import           Squeal.PostgreSQL
  import           GHC.Generics
  import qualified Generics.SOP                  as SOP
  
  
  -- Type
  
  data DeviceOS = Android | IOS
      deriving (Show, Read, Eq, Generic)
  -- DeviceOS is converted to PG Enum type
  instance SOP.Generic DeviceOS
  instance SOP.HasDatatypeInfo DeviceOS
  
  -- Defined extra types for the database
  -- Operating system enum
  type PGDeviceOS = PG (Enumerated DeviceOS)
  type DeviceOSType = 'Typedef PGDeviceOS
  
  -- For composite type
  data IPLocation = IPLocation
    { countryShort :: String
    , region       :: String
    , city         :: String
    }
    deriving (Show, Read, Eq, Generic)
  
  instance SOP.Generic IPLocation
  instance SOP.HasDatatypeInfo IPLocation
  
  -- IPLocation Composite type
  type PGIPLocation = PG (Composite IPLocation)
  type IPLocationType = 'Typedef PGIPLocation
  
  -- SCHEMA
  
  -- Users
  
  type UsersColumns = '[
      "id"   :::   'Def :=> 'NotNull 'PGint8
      , "email" ::: 'NoDef :=> 'NotNull 'PGtext
      , "password" ::: 'NoDef :=> 'NotNull 'PGtext
      , "first_name" ::: 'NoDef :=> 'Null 'PGtext
      , "birthyear" ::: 'NoDef :=> 'Null 'PGint2
      ]
  
  type UsersConstraints = '[
      "pk_users" ::: 'PrimaryKey '["id"]
      , "email" ::: 'Unique '["email"]
      ]
  
  type UsersTable = 'Table (UsersConstraints :=> UsersColumns)
  
  -- User devices
  type UserDevicesColumns = '[
    "id" ::: 'Def :=> 'NotNull 'PGint8 -- ID as PK because user might have many same OS devices
    , "user_id" ::: 'NoDef :=> 'NotNull 'PGint8
    , "token" ::: 'NoDef :=> 'NotNull 'PGtext
    , "os" ::: 'NoDef :=> 'NotNull PGDeviceOS
    ]
  
  type UserDevicesConstraints = '[
    "pk_user_devices" ::: 'PrimaryKey '["id"]
    , "fk_user_id" ::: 'ForeignKey '["user_id"] "public" "users" '["id"]
    , "token" ::: 'Unique '["token"]
    ]
  
  type UserDevicesTable = 'Table (UserDevicesConstraints :=> UserDevicesColumns)
  
  -- Schema
  -- Make sure to put types before tables, otherwise won't compile
  type Schema = '[
    -- Enum types:
      "device_os" ::: DeviceOSType
    -- Composite types:
    , "ip_location" ::: IPLocationType
    -- Tables:
    , "users" ::: UsersTable
    , "user_devices" ::: UserDevicesTable
    ]
  
  type Schemas = '["public" ::: Schema]
  