{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module Web.Hastodon.Types
  ( OAuthResponse(..)
  , Account(..)
  , AccountId(..)
  , Application(..)
  , Attachment(..)
  , AttachmentId(..)
  , Card(..)
  , Context(..)
  , Instance(..)
  , MediaId(..)
  , Mention(..)
  , Notification(..)
  , NotificationId(..)
  , OAuthClient(..)
  , OAuthClientId(..)
  , Relationship(..)
  , RelationshipId(..)
  , Report(..)
  , ReportId(..)
  , Results(..)
  , Status(..)
  , StatusId(..)
  , Tag(..)
  , OptionVal
  , OptionImpl
  , IsOption(..)
  ) where

import Data.Aeson
import Data.String (IsString, fromString)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Data.ByteString.Char8 as Char8
import qualified Data.Map as Map

newtype AccountId = AccountId { unAccountId :: String } deriving (FromJSON, IsString, Show)

newtype AttachmentId = AttachmentId { unAttachmentId :: String } deriving (FromJSON, IsString, Show)

newtype OAuthClientId = OAuthClientId { unOAuthClientId :: String } deriving (FromJSON, IsString, Show)

newtype MediaId = MediaId { unMediaId :: String } deriving (FromJSON, IsString, Show)

newtype NotificationId = NotificationId { unNotificationId :: String } deriving (FromJSON, IsString, Show)

newtype RelationshipId = RelationshipId { unRelationshipId :: String } deriving (FromJSON, IsString, Show)

newtype ReportId = ReportId { unReportId :: String } deriving (FromJSON, IsString, Show)

newtype StatusId = StatusId { unStatusId :: String } deriving (FromJSON, IsString, Show)


data OAuthResponse = OAuthResponse {
  accessToken :: String
  -- NOTE currently ignore other fields.
} deriving (Show)
instance FromJSON OAuthResponse where
  parseJSON (Object v) =
    OAuthResponse <$> (v .: "access_token")

data Account = Account {
  accountId :: AccountId,
  accountUsername :: String,
  accountAcct :: String,
  accountDisplayName :: String,
  accountLocked :: Bool,
  accountCreatedAt :: String,
  accountFollowersCount :: Int,
  accountFollowingCount :: Int,
  accountStatusesCount :: Int,
  accountNote :: String,
  accountUrl :: String,
  accountAvatar :: String,
  accountAvatarStatic :: String,
  accountHeader :: String,
  accountHeaderStatic :: String
} deriving (Show)
instance FromJSON Account where
  parseJSON (Object v) =
    Account <$> (v .: "id")
            <*> (v .: "username")
            <*> (v .: "acct")
            <*> (v .: "display_name")
            <*> (v .: "locked")
            <*> (v .: "created_at")
            <*> (v .: "followers_count")
            <*> (v .: "following_count")
            <*> (v .: "statuses_count")
            <*> (v .: "note")
            <*> (v .: "url")
            <*> (v .: "avatar")
            <*> (v .: "avatar_static")
            <*> (v .: "header")
            <*> (v .: "header_static")

data Application = Application {
  applicationName :: String,
  applicationWebsite :: Maybe String
} deriving (Show)
instance FromJSON Application where
  parseJSON (Object v) =
    Application <$> (v .:  "name")
                <*> (v .:? "website")

data Attachment = Attachment {
  attachmentId :: AttachmentId,
  attachmentType :: String,
  attachmentUrl :: String,
  attachmentRemoteUrl :: Maybe String,
  attachmentPreviewUrl :: String,
  attachmentTextUrl :: Maybe String
} deriving (Show)
instance FromJSON Attachment where
  parseJSON (Object v) =
    Attachment <$> (v .:  "id")
               <*> (v .:  "type")
               <*> (v .:  "url")
               <*> (v .:? "remote_url")
               <*> (v .:  "preview_url")
               <*> (v .:? "text_url")

data Card = Card {
  cardUrl :: String,
  cardTitle :: String,
  cardDescription :: String,
  cardImage :: String
} deriving (Show)
instance FromJSON Card where
  parseJSON (Object v) =
    Card <$> (v .: "url")
         <*> (v .: "title")
         <*> (v .: "description")
         <*> (v .: "image")

data Context = Context {
  contextAncestors :: [Status],
  contextDescendants :: [Status]
} deriving (Show)
instance FromJSON Context where
  parseJSON (Object v) =
    Context <$> (v .: "ancestors")
            <*> (v .: "descendants")

data Instance = Instance {
  instanceUri :: String,
  instanceTitle :: String,
  instanceDescription :: String,
  instanceEmail :: String
} deriving (Show)
instance FromJSON Instance where
  parseJSON (Object v) =
    Instance <$> (v .: "uri")
             <*> (v .: "title")
             <*> (v .: "description")
             <*> (v .: "email")

data Mention = Mention {
  mentionUrl :: String,
  mentionUsername :: String,
  mentionAcct :: String,
  mentionId :: AccountId
} deriving (Show)
instance FromJSON Mention where
  parseJSON (Object v) =
    Mention <$> (v .: "url")
            <*> (v .: "username")
            <*> (v .: "acct")
            <*> (v .: "id")

data Notification = Notification {
  notificationId :: NotificationId,
  notificationType :: String,
  notificationCreatedAt :: String,
  notificationAccount :: Account,
  notificationStatus :: Maybe Status
} deriving (Show)
instance FromJSON Notification where
  parseJSON (Object v) =
    Notification <$> (v .:  "id")
                 <*> (v .:  "type")
                 <*> (v .:  "created_at")
                 <*> (v .:  "account")
                 <*> (v .:? "status")

data OAuthClient = OAuthClient {
  oauthClientId :: OAuthClientId,
  oauthClientRedirectUri :: String,
  oauthClientClientId :: String,
  oauthClientClientSecret :: String
} deriving (Show)
instance FromJSON OAuthClient where
  parseJSON (Object v) =
    OAuthClient <$> (v .: "id")
                <*> (v .: "redirect_uri")
                <*> (v .: "client_id")
                <*> (v .: "client_secret")

data Relationship = Relationship {
  relationshipId :: RelationshipId,
  relationshipFollowing :: Bool,
  relationshipFollowed_by :: Bool,
  relationshipBlocking :: Bool,
  relationshipMuting :: Bool,
  relationshipRequested :: Bool
} deriving (Show)
instance FromJSON Relationship where
  parseJSON (Object v) =
    Relationship <$> (v .: "id")
                 <*> (v .: "following")
                 <*> (v .: "followed_by")
                 <*> (v .: "blocking")
                 <*> (v .: "muting")
                 <*> (v .: "requested")

data Report = Report {
  reportId :: ReportId,
  reportActionToken :: String
} deriving (Show)
instance FromJSON Report where
  parseJSON (Object v) =
    Report <$> (v .: "id")
           <*> (v .: "action_taken")

data Results = Results {
  resultAccounts :: [Account],
  resultStatus :: [Status],
  resultHashtags :: [String]
} deriving (Show)
instance FromJSON Results where
  parseJSON (Object v) =
    Results <$> (v .: "accounts")
            <*> (v .: "statuses")
            <*> (v .: "hashtags")

data Emoji = Emoji {
  emojiShortcode :: String,
  emojiStaticUrl :: String,
  emojiUrl :: String
} deriving (Show)
instance FromJSON Emoji where
 parseJSON (Object v) =
   Emoji <$> (v .: "shortcode")
         <*> (v .: "static_url")
         <*> (v .: "url")

data Status = Status {
  statusId :: StatusId,
  statusUri :: String,
  statusUrl :: String,
  statusAccount :: Account,
  statusInReplyToId :: Maybe Int,
  statusInReplyToAccountId :: Maybe Int,
  statusReblog :: Maybe Status,
  statusContent :: String,
  statusCreatedAt :: String,
  statusReblogsCount :: Int,
  statusFavouritesCount :: Int,
  statusReblogged :: Maybe Bool,
  statusFavourited :: Maybe Bool,
  statusMuted :: Maybe Bool,
  statusSensitive :: Maybe Bool,
  statusSpoilerText :: String,
  statusVisibility :: String,
  statusMediaAttachments :: [Attachment],
  statusMentions :: [Mention],
  statusTags :: [Tag],
  statusApplication :: Maybe Application,
  statusEmojis :: [Emoji],
  statusLanguage :: Maybe String
} deriving (Show)
instance FromJSON Status where
  parseJSON (Object v) =
    Status <$> (v .:  "id")
           <*> (v .:  "uri")
           <*> (v .:  "url")
           <*> (v .:  "account")
           <*> (v .:? "in_reply_to_id")
           <*> (v .:? "in_reply_to_account_id")
           <*> (v .:? "reblog")
           <*> (v .:  "content")
           <*> (v .:  "created_at")
           <*> (v .:  "reblogs_count")
           <*> (v .:  "favourites_count")
           <*> (v .:? "reblogged")
           <*> (v .:? "favourited")
           <*> (v .:? "muted")
           <*> (v .:? "sensitive")
           <*> (v .:  "spoiler_text")
           <*> (v .:  "visibility")
           <*> (v .:  "media_attachments")
           <*> (v .:  "mentions")
           <*> (v .:  "tags")
           <*> (v .:? "application")
           <*> (v .: "emojis")
           <*> (v .:? "language")

data Tag = Tag {
  name :: String,
  url :: String
} deriving (Show)
instance FromJSON Tag where
  parseJSON (Object v) =
    Tag <$> (v .: "name")
        <*> (v .: "url")

-- Left : Array parameter, Right : Single parameter
type OptionVal = Either [Char8.ByteString] (Maybe Char8.ByteString)

type OptionImpl = Map.Map Char8.ByteString OptionVal

class IsOption a where
  fromOptionImpl :: OptionImpl -> a
  toOptionImpl :: a -> OptionImpl