{-# LANGUAGE OverloadedStrings #-}

{-|
Module      : Hurriyet
Copyright   : (c) Yiğit Özkavcı, 2017
License     : WTFPL
Maintainer  : yigitozkavci8@gmail.com
Stability   : experimental
Portability : GHC

This module controls all the data flow and client interface required to interact with the library.
-}
module Hurriyet where

import Network.HTTP.Client
import Network.HTTP.Client.TLS
import Data.Text (Text)
import Data.ByteString.Lazy as L
import Data.ByteString.Internal as I
import Data.ByteString.Char8 as C
import Hurriyet.Services
import Data.Aeson (decode, eitherDecode)


{- Planned client usage:
   defaultOptions :: Options
   getClient :: I.ByteString -> Client
   getArticles :: Client -> IO (Either String [Articles])
-}

{-| Type synonym for Id which will be used for endpoints
-}
type Id = String

{-| Type synonym for api key
-}
type ApiKey = String

{-| Client will be used and passed through every
    API call
-}
newtype Client = Client
  { apiKey :: ApiKey
  }

{-| This is how you construct the client. Takes apiKey as
    an argument
-}
getClient :: String -> Client
getClient = Client

{-| Each of these resources represent services. These are
    used for passing service-spesific data such as endpoint string
-}
data Resource = ArticleResource
              | PageResource
              | NewsPhotoGalleryResource
              | ColumnResource
              | PathResource
              | WriterResource

{-| Each resource represents an endpoint string
-}
instance Show Resource where
  show ArticleResource          = "articles"
  show PageResource             = "pages"
  show NewsPhotoGalleryResource = "newsphotogalleries"
  show ColumnResource           = "columns"
  show PathResource             = "paths"
  show WriterResource           = "writers"

{-| For now, Hurriyet API only consists of 2 operations. As we have
    more, new operations will be added into here
-}
data Operation = List Resource
               | Show Resource Id

{-| Base url of the Hurriyet API
-}
baseUrl :: String
baseUrl =
  "https://api.hurriyet.com.tr/v1/"

{-| This method constructs url given the operation. Operation already
    contains enough data to construct the url
-}
getUrl :: Operation -> String
getUrl operation =
  case operation of
    List resource    -> baseUrl ++ show resource
    Show resource id -> baseUrl ++ show resource ++ "/" ++ id

{-| Given a client and a operation, this method can conquer the world. But prefers
    not to and returns a bytestring representing json string of the response body
-}
fetchResource :: Client -> Operation -> IO L.ByteString
fetchResource client operation = do
  manager <- newManager tlsManagerSettings
  initialRequest <- parseRequest $ getUrl operation
  let request = initialRequest { method = "GET", requestHeaders = [("apikey", C.pack $ apiKey client)] }
  response <- httpLbs request manager
  return $ responseBody response

{-| Get a single page
-}
getPage :: Client -> Id -> IO (Either String Page)
getPage client id =
  fetchResource client (Show PageResource id) >>= \str ->
    return $ eitherDecode str

{-| Get all pages
-}
getPages :: Client -> IO (Either String [Page])
getPages client =
  fetchResource client (List PageResource) >>= \str ->
    return $ eitherDecode str

{-| Get a single news photo gallery
-}
getNewsPhotoGallery :: Client -> Id -> IO (Either String NewsPhotoGallery)
getNewsPhotoGallery client id =
  fetchResource client (Show NewsPhotoGalleryResource id) >>= \str ->
    return $ eitherDecode str

{-| Get all news photo galleries
-}
getNewsPhotoGalleries :: Client -> IO (Either String [NewsPhotoGallery])
getNewsPhotoGalleries client =
  fetchResource client (List NewsPhotoGalleryResource) >>= \str ->
    return $ eitherDecode str

{-| Get a single column
-}
getColumn :: Client -> Id -> IO (Either String Column)
getColumn client id =
  fetchResource client (Show ColumnResource id) >>= \str ->
    return $ eitherDecode str

{-| Get all columns
-}
getColumns :: Client -> IO (Either String [Column])
getColumns client =
  fetchResource client (List ColumnResource) >>= \str ->
    return $ eitherDecode str

{-| Get a single path
-}
getPath :: Client -> Id -> IO (Either String Path)
getPath client id =
  fetchResource client (Show PathResource id) >>= \str ->
    return $ eitherDecode str

{-| Get all paths
-}
getPaths :: Client -> IO (Either String [Path])
getPaths client =
  fetchResource client (List PathResource) >>= \str ->
    return $ eitherDecode str

{-| Get a single writer
-}
getWriter :: Client -> Id -> IO (Either String Writer)
getWriter client id =
  fetchResource client (Show WriterResource id) >>= \str ->
    return $ eitherDecode str

{-| Get all writers
-}
getWriters :: Client -> IO (Either String [Writer])
getWriters client =
  fetchResource client (List WriterResource) >>= \str ->
    return $ eitherDecode str

{-| Get single article
-}
getArticle :: Client -> Id -> IO (Either String Article)
getArticle client id =
  fetchResource client (Show ArticleResource id) >>= \str ->
    return $ eitherDecode str

{-| Get all articles
-}
getArticles :: Client -> IO (Either String [Article])
getArticles client =
  fetchResource client (List ArticleResource) >>= \str ->
    return $ eitherDecode str