{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-unused-imports #-}

module SwaggerPetstore.Types (
  Amount (..),
  ApiResponse (..),
  Category (..),
  Currency (..),
  Order (..),
  Pet (..),
  Tag (..),
  User (..),
  ) where

import Data.List (stripPrefix)
import Data.Maybe (fromMaybe)
import Data.Aeson (Value, FromJSON(..), ToJSON(..), genericToJSON, genericParseJSON)
import Data.Aeson.Types (Options(..), defaultOptions)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Map as Map
import GHC.Generics (Generic)
import Data.Function ((&))


-- | some description 
data Amount = Amount
  { amountValue :: Double -- ^ some description 
  , amountCurrency :: Currency -- ^ 
  } deriving (Show, Eq, Generic)

instance FromJSON Amount where
  parseJSON = genericParseJSON (removeFieldLabelPrefix True "amount")
instance ToJSON Amount where
  toJSON = genericToJSON (removeFieldLabelPrefix False "amount")

-- | Describes the result of uploading an image resource
data ApiResponse = ApiResponse
  { apiResponseCode :: Int -- ^ 
  , apiResponseType :: Text -- ^ 
  , apiResponseMessage :: Text -- ^ 
  } deriving (Show, Eq, Generic)

instance FromJSON ApiResponse where
  parseJSON = genericParseJSON (removeFieldLabelPrefix True "apiResponse")
instance ToJSON ApiResponse where
  toJSON = genericToJSON (removeFieldLabelPrefix False "apiResponse")

-- | A category for a pet
data Category = Category
  { categoryId :: Integer -- ^ 
  , categoryName :: Text -- ^ 
  } deriving (Show, Eq, Generic)

instance FromJSON Category where
  parseJSON = genericParseJSON (removeFieldLabelPrefix True "category")
instance ToJSON Category where
  toJSON = genericToJSON (removeFieldLabelPrefix False "category")

-- | some description 
newtype Currency = Currency Text deriving (Show, Eq, FromJSON, ToJSON, Generic)

-- | An order for a pets from the pet store
data Order = Order
  { orderId :: Integer -- ^ 
  , orderPetId :: Integer -- ^ 
  , orderQuantity :: Int -- ^ 
  , orderShipDate :: Integer -- ^ 
  , orderStatus :: Text -- ^ Order Status
  , orderComplete :: Bool -- ^ 
  } deriving (Show, Eq, Generic)

instance FromJSON Order where
  parseJSON = genericParseJSON (removeFieldLabelPrefix True "order")
instance ToJSON Order where
  toJSON = genericToJSON (removeFieldLabelPrefix False "order")

-- | A pet for sale in the pet store
data Pet = Pet
  { petId :: Integer -- ^ 
  , petCategory :: Category -- ^ 
  , petName :: Text -- ^ 
  , petPhotoUrls :: [Text] -- ^ 
  , petTags :: [Tag] -- ^ 
  , petStatus :: Text -- ^ pet status in the store
  } deriving (Show, Eq, Generic)

instance FromJSON Pet where
  parseJSON = genericParseJSON (removeFieldLabelPrefix True "pet")
instance ToJSON Pet where
  toJSON = genericToJSON (removeFieldLabelPrefix False "pet")

-- | A tag for a pet
data Tag = Tag
  { tagId :: Integer -- ^ 
  , tagName :: Text -- ^ 
  } deriving (Show, Eq, Generic)

instance FromJSON Tag where
  parseJSON = genericParseJSON (removeFieldLabelPrefix True "tag")
instance ToJSON Tag where
  toJSON = genericToJSON (removeFieldLabelPrefix False "tag")

-- | A User who is purchasing from the pet store
data User = User
  { userId :: Integer -- ^ 
  , userUsername :: Text -- ^ 
  , userFirstName :: Text -- ^ 
  , userLastName :: Text -- ^ 
  , userEmail :: Text -- ^ 
  , userPassword :: Text -- ^ 
  , userPhone :: Text -- ^ 
  , userUserStatus :: Int -- ^ User Status
  } deriving (Show, Eq, Generic)

instance FromJSON User where
  parseJSON = genericParseJSON (removeFieldLabelPrefix True "user")
instance ToJSON User where
  toJSON = genericToJSON (removeFieldLabelPrefix False "user")

-- Remove a field label prefix during JSON parsing.
-- Also perform any replacements for special characters.
removeFieldLabelPrefix :: Bool -> String -> Options
removeFieldLabelPrefix forParsing prefix =
  defaultOptions
  {fieldLabelModifier = fromMaybe (error ("did not find prefix " ++ prefix)) . stripPrefix prefix . replaceSpecialChars}
  where
    replaceSpecialChars field = foldl (&) field (map mkCharReplacement specialChars)
    specialChars =
      [ ("@", "'At")
      , ("\\", "'Back_Slash")
      , ("<=", "'Less_Than_Or_Equal_To")
      , ("\"", "'Double_Quote")
      , ("[", "'Left_Square_Bracket")
      , ("]", "'Right_Square_Bracket")
      , ("^", "'Caret")
      , ("_", "'Underscore")
      , ("`", "'Backtick")
      , ("!", "'Exclamation")
      , ("#", "'Hash")
      , ("$", "'Dollar")
      , ("%", "'Percent")
      , ("&", "'Ampersand")
      , ("'", "'Quote")
      , ("(", "'Left_Parenthesis")
      , (")", "'Right_Parenthesis")
      , ("*", "'Star")
      , ("+", "'Plus")
      , (",", "'Comma")
      , ("-", "'Dash")
      , (".", "'Period")
      , ("/", "'Slash")
      , (":", "'Colon")
      , ("{", "'Left_Curly_Bracket")
      , ("|", "'Pipe")
      , ("<", "'LessThan")
      , ("!=", "'Not_Equal")
      , ("=", "'Equal")
      , ("}", "'Right_Curly_Bracket")
      , (">", "'GreaterThan")
      , ("~", "'Tilde")
      , ("?", "'Question_Mark")
      , (">=", "'Greater_Than_Or_Equal_To")
      ]
    mkCharReplacement (replaceStr, searchStr) = T.unpack . replacer (T.pack searchStr) (T.pack replaceStr) . T.pack
    replacer =
      if forParsing
        then flip T.replace
        else T.replace
