{-# LANGUAGE CPP                        #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE FlexibleContexts           #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE NoImplicitPrelude          #-}
{-# LANGUAGE OverloadedLists            #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE ScopedTypeVariables        #-}


module RFC.Data.IdAnd
  ( idAndsToMap
  , IdAnd(..)
  , valuesToIdAnd
  , idAndToTuple
  , tupleToIdAnd
  , idAndToPair
  , RefMap(..)
  ) where

import           RFC.Prelude


import           Data.Aeson                           as JSON
import qualified Data.List                            as List hiding ((++))
import qualified Data.Map                             as Map
import qualified Data.UUID.Types                      as UUID

#if MIN_VERSION_aeson(1,0,0)
  -- Don't need the backflips for maps
#else
import           Data.Aeson.Types                     (Parser, typeMismatch)
import           Data.Bitraversable
import qualified Data.HashMap.Lazy                    as HashMap
#endif

#ifndef GHCJS_BROWSER
import           Control.Lens                         hiding ((.=))
import           Data.Proxy                           (Proxy (..))
import           Data.Swagger
import           Database.PostgreSQL.Simple.FromField ()
import           Database.PostgreSQL.Simple.FromRow
import           Database.PostgreSQL.Simple.ToField
import           Database.PostgreSQL.Simple.ToRow
import           Servant.Docs
#endif

-- |Represents something which has an ID.
newtype IdAnd a = IdAnd (UUID, a)
  deriving (Eq, Ord, Show, Generic, Typeable)

newtype RefMap a = RefMap (Map.Map UUID (IdAnd a))
  deriving (Eq, Ord, Show, Generic, Typeable, FromJSON, ToJSON)

tupleToIdAnd :: (UUID, a) -> IdAnd a
tupleToIdAnd = IdAnd

valuesToIdAnd :: UUID -> a -> IdAnd a
valuesToIdAnd id a = IdAnd(id,a)

idAndToTuple :: IdAnd a -> (UUID, a)
idAndToTuple (IdAnd it) = it

idAndToPair :: IdAnd a -> (UUID, IdAnd a)
idAndToPair idAnd@(IdAnd (id,_)) = (id, idAnd)

idAndsToMap :: [IdAnd a] -> RefMap a
idAndsToMap list = RefMap $ Map.fromList $ List.map (\idAnd@(IdAnd(uuid,_)) -> (uuid,idAnd)) list

instance (FromJSON a) => FromJSON (IdAnd a) where
  parseJSON = JSON.withObject "IdAnd" $ \o -> do
    id <- o .: "id"
    value <- o .: "value"
    return $ IdAnd(id, value)

instance (ToJSON a) => ToJSON (IdAnd a) where
  toJSON (IdAnd (id,value)) = object [ "id".=id, "value".=value ]

#if MIN_VERSION_aeson(1,0,0)
  -- Have Mpa instances automatically created
#else
instance (FromJSON a) => FromJSON (Map UUID (IdAnd a)) where
  parseJSON (Object obj) =
      Map.fromList <$> listInParser
    where
      objList :: [(Text, Value)]
      objList = HashMap.toList obj
      die :: Text -> Parser UUID
      die k = fail . cs $ "Could not parse UUID: " ++ k
      mapMKey :: Text -> Parser UUID
      mapMKey k = maybe (die k) return $ UUID.fromText k
      mapMVal :: Value -> Parser (IdAnd a)
      mapMVal = parseJSON
      mapPair :: (Text,Value) -> Parser (UUID, IdAnd a)
      mapPair = bimapM mapMKey mapMVal
      parserList :: [Parser (UUID, IdAnd a)]
      parserList = map mapPair objList
      listInParser :: Parser [(UUID, IdAnd a)]
      listInParser = sequence parserList

  parseJSON invalid = typeMismatch "Map UUID (IdAnd a)" invalid


instance (ToJSON a) => ToJSON (Map UUID (IdAnd a)) where
  toJSON =
    Object . HashMap.fromList . map (\(k,v) -> (UUID.toText k, toJSON v)) . Map.toList

#endif

#ifndef GHCJS_BROWSER
instance (FromRow a) => FromRow (IdAnd a) where
  fromRow = valuesToIdAnd <$> field <*> fromRow

instance (ToRow a) => ToRow (IdAnd a) where
  toRow (IdAnd (id,a)) = toField id : toRow a

instance (ToSchema a, ToJSON a, ToSample a) => ToSchema (IdAnd a) where
  declareNamedSchema _ = do
    NamedSchema{..} <- declareNamedSchema (Proxy :: Proxy a)
    let aMaybeName =  _namedSchemaName
    aSchema <- declareSchemaRef (Proxy :: Proxy a)
    idSchema <- declareSchemaRef (Proxy :: Proxy UUID)
    let maybeSample = safeHead $ toSamples (Proxy :: Proxy (IdAnd a))
    return $ NamedSchema (map (\name -> "IdAnd " ++ name) aMaybeName) $
      mempty
        & type_ .~ SwaggerObject
        & properties .~ [("id", idSchema), ("value", aSchema)]
        & required .~ ["id", "value"]
        & example .~ (toJSON . snd <$> maybeSample)

instance (ToSchema a, ToJSON a, ToSample a) => ToSchema (RefMap a) where
  declareNamedSchema _ = do
    NamedSchema{..} <- declareNamedSchema (Proxy :: Proxy a)
    let aMaybeName =  _namedSchemaName
    idAndASchema <- declareSchemaRef (Proxy :: Proxy (IdAnd a))
    let maybeSample = safeHead $ toSamples (Proxy :: Proxy (RefMap a))
    return $ NamedSchema (map (\name -> "RefMap " ++ name) aMaybeName) $
      mempty
        & type_ .~ SwaggerObject
        & additionalProperties .~ (Just idAndASchema)
        & example .~ (toJSON . snd <$> maybeSample)

uuidList :: [UUID]
uuidList = List.cycle $ map (fromMaybe UUID.nil) $ map UUID.fromString
  [ "4fc2ffac-9100-41d6-94e1-c33a545e9ba2"
  , "1046948e-f8c7-4985-a008-fec4938696f4"
  , "83162802-598f-4de3-bd35-6c3fc0433965"
  , "1f628272-cfd8-4d43-ab00-ace5085164cc"
  , "64de6be2-4ca7-4ab7-ba3b-42d5dd0b79e7"
  , "20799832-f67d-4fd7-9435-58f3452f680d"
  , "a65c9782-863f-47cb-a2d4-438b8ade9e14"
  , "6e76c9a0-31f2-4bc7-a7b4-a2bb4a4dc03f"
  , "cc8b25ad-b596-4922-801a-2daeab69fbbf"
  , "59f44f8b-16e2-4839-b455-a1df01c704a5"
  , "7d8423c2-60a5-480f-b8b4-732b832bc80f"
  , "4b9f6e8c-c6c7-47f8-b1b6-13c987c17396"
  , "ae80805a-93bd-4f03-9c1e-d1ef0225b914"
  , "21ec1b66-97b7-47d4-9f08-2bb32ebf8a6f"
  , "799cdc04-aa86-45f9-91ae-24a7b9ea5302"
  , "babde8aa-055f-4936-b693-a32659f7ca63"
  , "99bde639-8ae1-40f5-a447-13531530f06e"
  , "67e7b91c-561f-4307-ae79-f5d7cc37fbe4"
  , "4a158750-2074-4056-9314-77d082f567d2"
  , "acc114a2-3307-4b52-80fb-36db3878b487"
  , "ec2a6366-b693-4b30-a888-de4a7e577c95"
  , "01ee4efb-6ffc-412d-aedc-c0a49a606918"
  , "25c8288d-68c1-4b5b-98a3-8af6932db9dc"
  , "7bc8dd7d-95e7-4bb1-bae9-fe2fb9e60ec9"
  , "9e6614a2-dc4f-4f97-9531-bfdbbdf70f15"
  , "fd0e77a8-fcdd-4c52-8b41-31fb4b137a46"
  , "b0395674-5b36-4ea7-98c3-a65c879cb580"
  , "0a4f6d8e-a72c-428d-8fbb-616510e1c772"
  , "4a01469f-285a-471c-9381-6ebeba00d4d9"
  , "5312022d-be32-4865-8d9b-cda95179ea2e"
  , "f2f72c71-77fd-4df2-a0fe-426f9749b96c"
  , "7cf30079-d3e1-4215-bbde-7ed81db1b6f6"
  , "31e491e4-0e41-45b2-994a-d18063c218ba"
  , "8b364c18-9e46-4776-ac78-ffa4e200104b"
  , "3e5b0676-8258-4e55-a3af-233d5ffac4c7"
  , "90f3d59f-42b8-476f-af4f-30abe465f510"
  , "46ad2bc5-f1bb-47e5-b336-8465eca1c3e1"
  , "8bfd88b5-f0c8-4e80-a25b-509c3d70f72e"
  , "eb9f9756-9431-4a0f-8df4-641f161f9450"
  , "99237a8c-14ae-4da3-a1d1-ee09add55746"
  , "3d474e6c-3402-46f6-8451-be3d6143890a"
  , "60745f1b-d0a3-4754-b958-e1067527b5c4"
  , "bd6dbf99-6c68-4ce4-a34b-daa3c4766487"
  , "ce81cc21-fba5-4b0b-8464-52ac5f4e6fd0"
  , "2c42e8b6-89db-4ea6-87c0-1a63e8908b91"
  , "6bfa8e14-dadd-4866-ae89-c51582622e68"
  , "9e100253-f807-40d1-9eea-73e7b50be390"
  , "ace8ebeb-9302-4a86-b29a-ec3a8aeaa73d"
  , "7c380f96-a427-46bc-af93-f4451d6fc072"
  , "e91a4d88-9565-4392-883e-e1afbf7b4e1d"
  , "1c9534e9-97d6-4fc8-b072-e536fc80e16a"
  , "30adcd09-9bcc-4b8f-be73-8d9d63b9254b"
  , "a27aed7a-0074-4535-8776-573890d4bbe8"
  , "a1342012-e505-4e4b-a245-08b04cb7a36f"
  , "cff3c2d5-01a4-4ccd-bf4c-4f7d6ccef6a6"
  , "10eb2b44-7b9b-4822-9b0b-ee152a506f99"
  , "f434e085-8c73-4832-ba99-e18a0a42a77f"
  , "7f029d88-22b0-4c4f-bee5-b8de9bd4b787"
  , "e71d3104-dee1-4926-8b29-c96955e46fa8"
  , "833bf188-d160-4dfc-a2ff-cb63b273a7dc"
  , "fbe1a7ca-4b5f-4eaf-a064-43de857625de"
  , "2d2daa9c-109c-456a-b94f-c55dd440d938"
  , "785db7db-5221-4ebf-86de-c91e5b3d5438"
  , "e504b605-07f8-4da2-bb5b-a86cdb9d117e"
  , "05cf172b-312b-4078-9bd8-77215b12d0b0"
  , "2b77b9f1-dfbe-4e8a-b710-25df48c2b8c7"
  , "ec402b1f-226e-481b-8e11-e52337a36481"
  , "54ef2193-197e-41b5-83ab-c0e516432f84"
  , "2d7115d1-89a9-4b83-856e-2dc8b6d5db26"
  , "84ea6913-0106-4ac1-9b84-ba621fc4b345"
  , "eb7a5343-9761-42b0-8258-4b658a36138e"
  , "64661121-b968-4122-89a3-de5be06f88a8"
  , "97cba1d1-528a-417a-86da-bb950d8480cb"
  , "af308922-76d8-47f4-ae97-0f2943b8952d"
  , "6dfb66ca-16c0-4029-bcde-2727f27832a0"
  , "66f9f38c-1f15-4e4e-a852-3be361a4a850"
  , "4ba8818f-c57c-4164-8fef-d38c5bf20da8"
  , "bad4a978-d1a1-40af-a5f2-e0d93888e423"
  , "3699f74a-f216-4c64-b8b2-9beb5c67f5ae"
  , "3c1e5beb-45be-4ead-b42a-4aca4927340d"
  , "5f1e6582-b8ee-44ac-b0db-2e93548b7a38"
  , "c7e39c12-5f9a-402c-abf8-9ee3b7726638"
  , "65407ef2-34a8-4a3c-b638-809125f4a4b4"
  , "542ffd56-a2b3-4548-a308-0946fc748f0b"
  , "cfeef25b-d9f7-4c1f-a9ca-6705df94b70d"
  , "181a9c36-e45a-4b48-bf7a-8a7c17208a40"
  , "ca4df7e6-4693-4854-87a9-689ccb5642be"
  , "1d0ade67-4063-4cc3-aa91-72339432a531"
  , "8e9dcc80-80a2-43ea-969d-53a7e278a022"
  , "73bc56a1-7d1d-41f4-9254-42a1656c7558"
  , "92cdb456-85a1-4fa0-8603-35b9da3a7361"
  , "be181705-4382-41dd-96a5-a61f05cd1059"
  , "fb2c419c-ea3a-4691-a45a-bcd875154f1d"
  , "bbe1a0d3-5615-47df-81ca-b52e066f01e1"
  , "c45f6fcd-b032-4039-9c23-0185f52f5722"
  , "8c250ca1-3a13-401d-8907-49db8f8fd1b1"
  , "958f7a5c-0938-414a-a280-daed6227abeb"
  , "b1bf5322-4729-497e-a227-7ca40d3c3360"
  , "0636f9aa-c831-4882-8af5-a63c0081f683"
  , "3e79509a-c7ce-45a3-8e2a-87d99711612f"
  ]

instance (ToSample a) => ToSample (IdAnd a) where
  toSamples _ =
      map idAndify $ zip uuidList (toSamples Proxy)
    where
      idAndify (uuid, (desc, a)) = (desc, IdAnd (uuid,a))

instance (ToSample a) => ToSample (RefMap a) where
  toSamples _ = singleSample $
      RefMap $ Map.fromList $ map idAndify $ zip uuidList (toSamples Proxy)
    where
      idAndify (uuid, (_, a)) = (uuid, IdAnd (uuid,a))

#endif