{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}

module Web.Tracker.Story where

--------------------------------------------------------------------------------
import           Data.Aeson
import           Data.Aeson.Casing
import           Data.String
import           Data.Text         (Text)
import qualified Data.Text         as T
import           Data.Time.Clock
import           GHC.Generics
import           Servant.API
import           Text.Read
--------------------------------------------------------------------------------
data StoryId
  = StoryId Integer
  deriving (Show, Eq, Ord)

instance FromJSON StoryId where
  parseJSON = fmap StoryId . parseJSON

instance Read StoryId where
  readPrec = fmap StoryId readPrec

instance ToJSON StoryId where
  toJSON (StoryId t) = toJSON t

instance ToHttpApiData StoryId where
  toUrlPiece (StoryId t) = toUrlPiece t

data ProjectId
  = ProjectId Int
  deriving (Show, Eq, Ord)

instance FromJSON ProjectId where
  parseJSON = fmap ProjectId . parseJSON

instance Read ProjectId where
  readPrec = fmap ProjectId readPrec

instance ToHttpApiData ProjectId where
  toUrlPiece (ProjectId t) = toUrlPiece t

data UserId
  = UserId Int
  deriving (Show, Eq, Ord)

instance FromJSON UserId where
  parseJSON = fmap UserId . parseJSON

data StoryState
  = Accepted | Delivered | Finished | Started
  | Rejected | Planned | Unstarted | Unscheduled
  deriving (Show, Eq, Ord, Enum, Bounded)

instance FromJSON StoryState where
  parseJSON = withText "StoryState" $ \case
   "accepted"    -> return Accepted
   "delivered"   -> return Delivered
   "finished"    -> return Finished
   "started"     -> return Started
   "rejected"    -> return Rejected
   "planned"     -> return Planned
   "unstarted"   -> return Unstarted
   "unscheduled" -> return Unscheduled
   other -> fail $ "Invalid story state: " ++ T.unpack other

instance ToJSON StoryState where
  toJSON = toJSON . toUrlPiece

instance ToHttpApiData StoryState where
  toUrlPiece = \case
    Accepted    -> "accepted"
    Delivered   -> "delivered"
    Finished    -> "finished"
    Started     -> "started"
    Rejected    -> "rejected"
    Planned     -> "planned"
    Unstarted   -> "unstarted"
    Unscheduled -> "unscheduled"

data StoryType
  = Feature | Bug | Chore | Release
  deriving (Show, Eq, Ord)

instance FromJSON StoryType where
  parseJSON = withText "StoryType" $ \case
   "feature" -> return Feature
   "bug"     -> return Bug
   "chore"   -> return Chore
   "release" -> return Release
   other -> fail $ "Invalid StoryType: " ++ T.unpack other

data UpdateStory
  = SetStoryState StoryState

instance ToJSON UpdateStory where
  toJSON (SetStoryState state)
    = object [ "current_state" .= state ]

data Story = Story
  { sId            :: StoryId
  , sProjectId     :: ProjectId
  , sName          :: Text
  , sDescription   :: Maybe Text
  , sStoryType     :: StoryType
  , sCurrentState  :: StoryState
  , sEstimate      :: Maybe Double
  , sAcceptedAt    :: Maybe UTCTime
  -- , sDeadline            :: UTCTime
  -- , sProjectedCompletion :: UTCTime
  , sRequestedById :: UserId
  , sOwnerIds      :: [UserId]
  -- , sLabels        :: [Label]
  -- , sFollowerIds   :: [UserId]
  , sCreatedAt     :: UTCTime
  , sUpdatedAt     :: UTCTime
  -- , sBeforeId :: UTCTime
  -- , sAfterId :: UTCtime
  -- , sIntegrationId
  -- , sExternalId
  , sUrl           :: Text
  } deriving (Show, Generic)

instance FromJSON Story where
  parseJSON = genericParseJSON $ aesonPrefix snakeCase