module ServerCommunication
  ( Communication(..)
  , sendRequest
  , serverCommunications
  ) where

import Signal
import Task as Task exposing (Task)
import Http
import Json.Decode exposing (..)
import Date

import Model.Message exposing (messageDecoder)
import Model.User exposing (UserId)
import Model.Payment exposing (..)
import Model.View.Payment.Add exposing (Frequency)

import Update as U
import Update.SignIn exposing (..)
import Update.LoggedView as UL

type Communication =
  NoCommunication
  | SignIn String
  | AddPayment UserId String Int Frequency
  | DeletePayment PaymentId UserId Int Int
  | UpdatePage Int
  | SignOut

serverCommunications : Signal.Mailbox Communication
serverCommunications = Signal.mailbox NoCommunication

sendRequest : Communication -> Task Http.RawError U.Action
sendRequest communication =
  case getRequest communication of
    Nothing ->
      Task.succeed U.NoOp
    Just request ->
      Http.send Http.defaultSettings request
        |> flip Task.andThen (serverResult communication)

getRequest : Communication -> Maybe Http.Request
getRequest communication =
  case communication of
    NoCommunication ->
      Nothing
    SignIn login ->
      Just (simple "post" ("/signIn?login=" ++ login))
    AddPayment userId name cost frequency ->
      Just (simple "post" ("/payment/add?name=" ++ name ++ "&cost=" ++ (toString cost) ++ "&frequency=" ++ (toString frequency)))
    DeletePayment paymentId _ _ _ ->
      Just (simple "post" ("payment/delete?id=" ++ (toString paymentId)))
    UpdatePage page ->
      Just (updatePageRequest page)
    SignOut ->
      Just (simple "post"  "/signOut")

updatePageRequest : Int -> Http.Request
updatePageRequest page =
  simple "get" ("payments?page=" ++ toString page ++ "&perPage=" ++ toString perPage)

simple : String -> String -> Http.Request
simple method url =
  { verb = method
  , headers = []
  , url = url
  , body = Http.empty
  }

serverResult : Communication -> Http.Response -> Task Http.RawError U.Action
serverResult communication response =
  if response.status == 200
    then
      case communication of
        NoCommunication ->
          Task.succeed U.NoOp
        SignIn login ->
          Task.succeed (U.UpdateSignIn (ValidLogin login))
        AddPayment userId name cost frequency ->
          decodeResponse
            response
            ("id" := paymentIdDecoder)
            (\paymentId ->
              Http.send Http.defaultSettings (updatePageRequest 1)
                |> flip Task.andThen (\response2 ->
                     if response2.status == 200
                       then
                         decodeResponse
                           response2
                           paymentsDecoder
                           (\payments -> Task.succeed <| U.UpdateLoggedView (UL.AddPayment userId paymentId name cost frequency payments))
                       else
                         Task.succeed U.NoOp
                   )
            )
        DeletePayment id userId cost currentPage ->
          Http.send Http.defaultSettings (updatePageRequest currentPage)
            |> flip Task.andThen (\response ->
                 if response.status == 200
                   then
                     decodeResponse
                       response
                       paymentsDecoder
                       (\payments -> Task.succeed <| U.UpdateLoggedView (UL.Remove userId cost payments))
                   else
                     Task.succeed U.NoOp
               )
        UpdatePage page ->
          decodeResponse
            response
            paymentsDecoder
            (\payments -> Task.succeed <| U.UpdateLoggedView (UL.UpdatePage page payments))
        SignOut ->
          Task.succeed (U.GoSignInView)
    else
      decodeResponse
        response
        messageDecoder
        (\error ->
          case communication of
            SignIn _ ->
              Task.succeed <| U.UpdateSignIn (ErrorLogin error)
            _ ->
              Task.succeed <| U.NoOp
        )

decodeResponse : Http.Response -> Decoder a -> (a -> Task b U.Action) -> Task b U.Action
decodeResponse response decoder responseToAction =
  case response.value of
    Http.Text text ->
      case decodeString decoder text of
        Ok x ->
          responseToAction x
        Err _ ->
          Task.succeed U.NoOp
    Http.Blob _ ->
      Task.succeed U.NoOp