module Leankit.Api 
	(
		-- * How to use this library
		-- $use
		
		-- * getBoards
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Boards@
		getBoards,
		getBoardsMaybe,
		getBoardsEither,

		-- * getBoard
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Boards\/{boardId}@
		getBoard,
		getBoardMaybe,
		getBoardEither,

		-- * getCard
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Board\/{boardId}\/GetCard\/{cardId}@
		getCard,
		getCardMaybe,
		getCardEither,

		-- * getBoardIdentifiers
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Board\/{boardId}\/GetBoardIdentifiers@
		getBoardIdentifiers,
		getBoardIdentifiersMaybe,
		getBoardIdentifiersEither,

		-- * getNewerIfExists
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Board\/{boardId}\/BoardVersion/{version}/GetNewerIfExists@
		getNewerIfExists,
		getNewerIfExistsMaybe,
		getNewerIfExistsEither,

		-- * getBoardHistorySince
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Board\/{boardId}\/BoardVersion/{version}/GetBoardHistorySince@
		getBoardHistorySince,
		getBoardHistorySinceMaybe,
		getBoardHistorySinceEither,

		-- * getCardByExternalId
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Board\/{boardId}\/GetCardByExternalId\/BZ123@
		getCardByExternalId,
		getCardByExternalIdMaybe,
		getCardByExternalIdEither,

		-- * getBackLog
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Board\/{boardId}\/Backlog@
		getBackLog,
		getBackLogMaybe,
		getBackLogEither,

		-- * getArchive
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Board\/{boardId}\/Archive@
		getArchive,
		getArchiveMaybe,
		getArchiveEither,

		-- * getCardHistory
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Card\/History\/{boardId}\/{cardId}@
		getCardHistory,
		getCardHistoryMaybe,
		getCardHistoryEither,

		-- * getCardComments
		-- | API functions for : @http:\/\/myaccount.leankit.com\/Api\/Card\/GetComments\/{boardId}\/{cardId}@
		getCardComments,
		getCardCommentsMaybe,
		getCardCommentsEither,

		-- * Credentials
		-- | This symbol is defined as 'Leankit.Types.Credentials.Credentials'
		Credentials (Credentials)
	) where

import Data.ByteString.Lazy.Internal (ByteString)
import Network.Curl

import Control.Applicative ((<$>))

import Data.Aeson
import Leankit.Types
import Leankit.Types.Common
import Leankit.Types.Credentials
import Leankit.Types.Board (Board)
import Leankit.Types.BoardShort (BoardShort)
import Leankit.Types.Card (Card)
import Leankit.Types.BoardIdentifierSet (BoardIdentifierSet)
import Leankit.Types.BoardHistoryItem (BoardHistoryItem)
import Leankit.Types.Lane (Lane)
import Leankit.Types.LaneLayout (LaneLayout)
import Leankit.Types.CardHistoryItem (CardHistoryItem)
import Leankit.Types.CardComment (CardComment)

-- TODO what can we do with this?
(<=<) :: Monad m => (b->m c) -> (a -> m b) -> a -> m c
(<=<) fbc fab a = fbc =<< fab a

(<==<) :: Monad m => (b->m c) -> (a1 -> a2 -> m b) -> a1 -> a2 -> m c
(<==<) fbc fab a1 a2 = fbc =<< fab a1 a2

(<===<) :: Monad m => (b->m c) -> (a1 -> a2 -> a3 -> m b) -> a1 -> a2 -> a3 -> m c
(<===<) fbc fab a1 a2 a3 = fbc =<< fab a1 a2 a3

---------
-- API --
---------

getBoards :: Credentials -> IO [BoardShort]
getBoards = _either2fail <=< getBoardsEither

getBoardsMaybe :: Credentials -> IO (Maybe [BoardShort])
getBoardsMaybe = _either2maybe <=< getBoardsEither

getBoardsEither :: Credentials -> IO (Either String [BoardShort])
getBoardsEither creds = _apiCallEither creds "/Boards/"


-- getBoard
getBoard :: Credentials -> BoardID -> IO Board
getBoard = _either2fail <==< getBoardEither

getBoardMaybe :: Credentials -> BoardID -> IO (Maybe Board)
getBoardMaybe = _either2maybe <==< getBoardEither

getBoardEither :: Credentials -> BoardID -> IO (Either String Board)
getBoardEither cred (BoardID boardID) = _apiCallEither cred ("/Boards/" ++ show boardID)


-- getCard
getCard :: Credentials -> BoardID -> CardID -> IO Card
getCard = _either2fail <===< getCardEither

getCardMaybe :: Credentials -> BoardID -> CardID -> IO (Maybe Card)
getCardMaybe = _either2maybe <===< getCardEither

getCardEither :: Credentials -> BoardID -> CardID -> IO (Either String Card)
getCardEither cred boardID (CardID cardID) = _boardApiCallEither cred boardID ("/GetCard/" ++ show cardID ++ "/")


-- getBoardIdentifiers
getBoardIdentifiers :: Credentials -> BoardID -> IO BoardIdentifierSet
getBoardIdentifiers = _either2fail <==< getBoardIdentifiersEither

getBoardIdentifiersMaybe :: Credentials -> BoardID -> IO (Maybe BoardIdentifierSet)
getBoardIdentifiersMaybe = _either2maybe <==< getBoardIdentifiersEither

getBoardIdentifiersEither :: Credentials -> BoardID -> IO (Either String BoardIdentifierSet)
getBoardIdentifiersEither cred boardID = _boardApiCallEither cred boardID "/GetBoardIdentifiers/"


-- getNewerIfExists
-- NOTE that this function returns with Nothing not only when an error occures
--      but when there is no newer board than the given version.
--      This is why ``getNewerIfExists`` returns with ``Maybe Board`` already.
getNewerIfExists :: Credentials -> BoardID -> Int -> IO (Maybe Board)
getNewerIfExists = getNewerIfExistsMaybe

getNewerIfExistsMaybe :: Credentials -> BoardID -> Int -> IO (Maybe Board)
getNewerIfExistsMaybe = _either2maybe <===< getNewerIfExistsEither

getNewerIfExistsEither :: Credentials -> BoardID -> Int -> IO (Either String Board)
getNewerIfExistsEither cred boardID boardVersion = 
	_boardApiCallEither cred boardID ("/BoardVersion/" ++ show boardVersion ++ "/GetNewerIfExists/")


-- getBoardHistorySince
getBoardHistorySince :: Credentials -> BoardID -> Int -> IO [BoardHistoryItem]
getBoardHistorySince = _either2fail <===< getBoardHistorySinceEither

getBoardHistorySinceMaybe :: Credentials -> BoardID -> Int -> IO (Maybe [BoardHistoryItem])
getBoardHistorySinceMaybe = _either2maybe <===< getBoardHistorySinceEither

getBoardHistorySinceEither :: Credentials -> BoardID -> Int -> IO (Either String [BoardHistoryItem])
getBoardHistorySinceEither cred boardID boardVersion =
	_boardApiCallEither cred boardID ("/BoardVersion/" ++ show boardVersion ++ "/GetBoardHistorySince/")


-- getCardByExternalId
getCardByExternalId :: Credentials -> BoardID -> String -> IO Card
getCardByExternalId = _either2fail <===< getCardByExternalIdEither

getCardByExternalIdMaybe :: Credentials -> BoardID -> String -> IO (Maybe Card)
getCardByExternalIdMaybe = _either2maybe <===< getCardByExternalIdEither

getCardByExternalIdEither :: Credentials -> BoardID -> String -> IO (Either String Card)
getCardByExternalIdEither cred boardID externalID = _boardApiCallEither cred boardID ("/GetCardByExternalId/" ++ show externalID ++ "/")


-- getBackLog
getBackLog :: Credentials -> BoardID -> IO [Lane]
getBackLog = _either2fail <==< getBackLogEither

getBackLogMaybe :: Credentials -> BoardID -> IO (Maybe [Lane])
getBackLogMaybe = _either2maybe <==< getBackLogEither

getBackLogEither :: Credentials -> BoardID -> IO (Either String [Lane])
getBackLogEither cred boardID = _boardApiCallEither cred boardID "/Backlog/"


-- getArchive
getArchive :: Credentials -> BoardID -> IO [LaneLayout]
getArchive = _either2fail <==< getArchiveEither

getArchiveMaybe :: Credentials -> BoardID -> IO (Maybe [LaneLayout])
getArchiveMaybe = _either2maybe <==< getArchiveEither

getArchiveEither :: Credentials -> BoardID -> IO (Either String [LaneLayout])
getArchiveEither cred boardID = _boardApiCallEither cred boardID "/Archive/"


-- getCardHistory
getCardHistory :: Credentials -> BoardID -> CardID -> IO [CardHistoryItem]
getCardHistory = _either2fail <===< getCardHistoryEither

getCardHistoryMaybe :: Credentials -> BoardID -> CardID -> IO (Maybe [CardHistoryItem])
getCardHistoryMaybe = _either2maybe <===< getCardHistoryEither

getCardHistoryEither :: Credentials -> BoardID -> CardID -> IO (Either String [CardHistoryItem])
getCardHistoryEither cred (BoardID boardID) (CardID cardID) = 
	_apiCallEither cred ("/Card/History/" ++ show boardID ++ "/" ++ show cardID ++ "/")


-- getCardComments
getCardComments :: Credentials -> BoardID -> CardID -> IO [CardComment]
getCardComments = _either2fail <===< getCardCommentsEither

getCardCommentsMaybe :: Credentials -> BoardID -> CardID -> IO (Maybe [CardComment])
getCardCommentsMaybe = _either2maybe <===< getCardCommentsEither

getCardCommentsEither :: Credentials -> BoardID -> CardID -> IO (Either String [CardComment])
getCardCommentsEither cred (BoardID boardID) (CardID cardID) = 
	_apiCallEither cred ("/Card/GetComments/" ++ show boardID ++ "/" ++ show cardID ++ "/")


-------------
-- PRIVATE --
-------------

_either2fail :: FromJSON a => Either String a -> IO a
_either2fail (Left err) = fail err
_either2fail (Right res) = return res

_either2maybe :: FromJSON a => Either String a -> IO (Maybe a)
_either2maybe (Left _) = return Nothing
_either2maybe (Right res) = return $ Just res

_boardApiCallEither :: FromJSON a => Credentials -> BoardID -> String -> IO (Either String a)
_boardApiCallEither cred (BoardID boardID) urlfrag = 
	_apiCallEither cred ("/Board/" ++ show boardID ++ urlfrag) 
	
_apiCallEither :: FromJSON a => Credentials -> String -> IO (Either String a)
_apiCallEither creds url = parseReplyData <$> _loadPath url creds

_loadPath :: String -> Credentials -> IO ByteString
_loadPath lpath cred = do
        (_, body) <- curlGetString_ url opts
        return body
    where
        url = "http://" ++ _company cred ++ ".leankitkanban.com/Kanban/Api" ++ lpath
        opts = [CurlUserPwd authstr]
        authstr = _username cred ++ ":" ++ _password cred

-- $use
-- This is a thin read-only library for Leankit API. It's using 'curl' for network and 
-- Getting all the boards:
-- 
-- > import Leankit.Api
-- > import Leankit.Types.Common
-- > let c = Credentials "myaccount" "username" "password"
-- > getBoardEither c $ BoardID 12345