package com.eddsteel.posts.leastpower
package endpoints

import model._
import services._
import webapp._

import org.scalatest.{FlatSpec, Matchers}
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito.{when, verify, RETURNS_SMART_NULLS}

import scala.concurrent.{Future, ExecutionContext}
import java.time.{ZoneOffset, LocalDate, ZonedDateTime, ZoneId}

class EndpointTestsV1 extends FlatSpec with Matchers with MockitoSugar with ScalaFutures {

  import ExecutionContext.Implicits.global

  // mock out our dependencies.
  // if we don't provide "RETURNS_SMART_NULLS" no effort will be taken
  // to explain the NullPointerExceptions thrown by misconfigured mocks.
  //
  val mockUserService = mock[UserService](RETURNS_SMART_NULLS)
  val mockGiftService = mock[GiftService](RETURNS_SMART_NULLS)
  val controller = new Controller with EndpointsV1 with UserServiceComponent with GiftServiceComponent {
    override val userService = mockUserService
    override val giftService = mockGiftService

    // all routes go to the endpoint we're testing.
    override def route(method: Method, path: String): Action = postGift
  }

  "POST /users/:id/gift" should "order a gift for a user on their birthday" in {
    // make the world ready for our test
    //
    DateTimeUtils.setDateTime(ZonedDateTime.of(2015, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC))

    // mock the return of a user that contains the right
    // information (and a bunch of irrelevant stuff too, because we
    // have to)
    //
    when(mockUserService.getById(1L)).thenReturn(
      Future.successful(Some(
        User(id = 1, birthday = LocalDate.of(1980, 1, 1), favoriteThing = "booze",
             timezone = ZoneOffset.UTC, pets = Set.empty, whichFriendAreYou = Friends.Phoebe))))

    // mock the gift service to just return a message saying it
    // successfully ordered the favorite thing (we're not testing this
    // service)
    //
    when(mockGiftService.order(1L, "booze")).thenReturn(
      Future.successful(
        ("booze ordered for user 1", Some(1L))))

    // Send the request through the routing logic to the endpoint to test the endpoint
    // logic.
    //
    val response = WebApp.postFakeRequest(controller, POST, "/users/1/gift", "")

    response.futureValue.shouldEqual("(booze ordered for user 1,Some(1))")
    verify(mockUserService).getById(1L)
    verify(mockGiftService).order(1L, "booze") // check the gift actually was ordered through the service
  }
}