{- This file is part of funbot-ext-events.
 -
 - Written in 2015 by fr33domlover <fr33domlover@rel4tion.org>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

-- For json field names
{-# LANGUAGE OverloadedStrings #-}

module FunBot.ExtEvents
    ( Branch (..)
    , Commit (..)
    , Push (..)
    , Tag (..)
    , MergeRequest (..)
    , NewsItem (..)
    , Paste (..)
    , ExtEvent (..)
    )
where

import Control.Applicative
import Control.Monad (mzero)
import Data.Aeson
import Data.Aeson.Types (Parser)

import qualified Data.Text as T

data Branch = Branch
    { branchName      :: String
    , branchRepo      :: String
    , branchRepoOwner :: String
    }
    deriving Show

data Commit = Commit
    { commitAuthor :: String
    , commitTitle  :: String
    , commitUrl    :: String
    }
    deriving Show

data Push = Push
    { pushBranch  :: Branch
    , pushCommits :: [Commit]
    }
    deriving Show

data Tag = Tag
    { tagAuthor    :: String
    , tagRef       :: String
    , tagRepo      :: String
    , tagRepoOwner :: String
    }
    deriving Show

data MergeRequest = MergeRequest
    { mrAuthor    :: String
    , mrId        :: Int
    , mrRepo      :: String
    , mrRepoOwner :: String
    , mrTitle     :: String
    , mrUrl       :: String
    , mrAction    :: String
    }
    deriving Show

data NewsItem = NewsItem
    { itemFeedLabel :: String
    , itemFeedTitle :: Maybe String
    , itemTitle     :: String
    , itemAuthor    :: Maybe String
    , itemUrl       :: Maybe String
    }
    deriving Show

data Paste = Paste
    { pasteAuthor  :: String
    , pasteVerb    :: String
    , pasteTitle   :: String
    , pasteUrl     :: String
    , pasteChannel :: String
    }
    deriving Show

-- | An event coming from one of the external event sources.
data ExtEvent
    = GitPushEvent Push
    | GitTagEvent Tag
    | MergeRequestEvent MergeRequest
    | NewsEvent NewsItem
    | PasteEvent Paste
    deriving Show

instance FromJSON Branch where
    parseJSON (Object o) =
        Branch <$>
        o .: "name" <*>
        o .: "repo" <*>
        o .: "user"
    parseJSON _          = mzero

instance ToJSON Branch where
    toJSON (Branch name repo owner) = object
        [ "name" .= name
        , "repo" .= repo
        , "user" .= owner
        ]

instance FromJSON Commit where
    parseJSON (Object o) =
        Commit <$>
        o .: "author" <*>
        o .: "title" <*>
        o .: "url"
    parseJSON _          = mzero

instance ToJSON Commit where
    toJSON (Commit author title url) = object
        [ "author" .= author
        , "title"  .= title
        , "url"    .= url
        ]

instance FromJSON Push where
    parseJSON (Object o) =
        Push <$>
        o .: "branch" <*>
        o .: "commits"
    parseJSON _          = mzero

instance ToJSON Push where
    toJSON (Push branch commits) = object
        [ "branch"  .= branch
        , "commits" .= commits
        ]

instance FromJSON Tag where
    parseJSON (Object o) =
        Tag <$>
        o .: "author" <*>
        o .: "ref" <*>
        o .: "repo" <*>
        o .: "user"
    parseJSON _          = mzero

instance ToJSON Tag where
    toJSON tag = object
        [ "author" .= tagAuthor tag
        , "ref"    .= tagRef tag
        , "repo"   .= tagRepo tag
        , "user"   .= tagRepoOwner tag
        ]

instance FromJSON MergeRequest where
    parseJSON (Object o) =
        MergeRequest <$>
        o .: "author" <*>
        o .: "id" <*>
        o .: "repo" <*>
        o .: "user" <*>
        o .: "title" <*>
        o .: "url" <*>
        o .: "action"
    parseJSON _          = mzero

instance ToJSON MergeRequest where
    toJSON mr = object
        [ "author" .= mrAuthor mr
        , "id"     .= mrId mr
        , "repo"   .= mrRepo mr
        , "user"   .= mrRepoOwner mr
        , "title"  .= mrTitle mr
        , "url"    .= mrUrl mr
        , "action" .= mrAction mr
        ]

instance FromJSON NewsItem where
    parseJSON (Object o) =
        NewsItem <$>
        o .: "feed-label" <*>
        o .: "feed-title" <*>
        o .: "title" <*>
        o .: "author" <*>
        o .: "url"
    parseJSON _          = mzero

instance ToJSON NewsItem where
    toJSON (NewsItem fLabel fTitle title author url) = object
        [ "feed-label" .= fLabel
        , "feed-title" .= fTitle
        , "title"      .= title
        , "author"     .= author
        , "url"        .= url
        ]

instance FromJSON Paste where
    parseJSON (Object o) =
        Paste <$>
        o .: "author" <*>
        o .: "verb" <*>
        o .: "title" <*>
        o .: "url" <*>
        o .: "channel"
    parseJSON _          = mzero

instance ToJSON Paste where
    toJSON (Paste author verb title url chan) = object
        [ "author"  .= author
        , "verb"    .= verb
        , "title"   .= title
        , "url"     .= url
        , "channel" .= chan
        ]

text :: Parser T.Text -> T.Text -> Parser T.Text
text parser expected = do
    got <- parser
    if got == expected
        then return got
        else mzero

instance FromJSON ExtEvent where
    parseJSON (Object o) =
        let kind = text $ o .: "type"
        in  kind "push"  *> (GitPushEvent      <$> o .: "data") <|>
            kind "tag"   *> (GitTagEvent       <$> o .: "data") <|>
            kind "mr"    *> (MergeRequestEvent <$> o .: "data") <|>
            kind "news"  *> (NewsEvent         <$> o .: "data") <|>
            kind "paste" *> (PasteEvent        <$> o .: "data")
    parseJSON _          = mzero

instance ToJSON ExtEvent where
    toJSON (GitPushEvent commits) = object [ "type" .= ("push" :: T.Text)
                                           , "data" .= commits
                                           ]
    toJSON (GitTagEvent tag)      = object [ "type" .= ("tag" :: T.Text)
                                           , "data" .= tag
                                           ]
    toJSON (MergeRequestEvent mr) = object [ "type" .= ("mr" :: T.Text)
                                           , "data" .= mr
                                           ]
    toJSON (NewsEvent item)       = object [ "type" .= ("news" :: T.Text)
                                           , "data" .= item
                                           ]
    toJSON (PasteEvent paste)     = object [ "type" .= ("paste" :: T.Text)
                                           , "data" .= paste
                                           ]