_ = require 'underscore-plus'
InboxLongConnection = require '../src/flux/inbox-long-connection'
InboxSyncWorker = require '../src/flux/inbox-sync-worker'
Thread = require '../src/flux/models/thread'

describe "InboxSyncWorker", ->
  beforeEach ->
    @apiRequests = []
    @api =
      getCollection: (namespace, model, params, requestOptions) =>
        @apiRequests.push({namespace, model, params, requestOptions})
      getThreads: (namespace, params, requestOptions) =>
        @apiRequests.push({namespace, model:'threads', params, requestOptions})

    @state =
      "contacts": {busy: true}
      "calendars": {complete: true}

    spyOn(atom.config, 'get').andCallFake (key) =>
      expected = "inbox.namespace-id.worker-state"
      return throw new Error("Not stubbed!") unless key is expected
      return @state

    spyOn(atom.config, 'set').andCallFake (key, val) =>
      expected = "inbox.namespace-id.worker-state"
      return throw new Error("Not stubbed!") unless key is expected
      @state = val

    @worker = new InboxSyncWorker(@api, 'namespace-id')
    @connection = @worker.connection()

  describe "start", ->
    it "should open the long polling connection", ->
      spyOn(@connection, 'start')
      @worker.start()
      expect(@connection.start).toHaveBeenCalled()

    it "should start querying for model collections that haven't been fully cached", ->
      @worker.start()
      expect(@apiRequests.length).toBe(3)
      modelsRequested = _.map @apiRequests, (r) -> r.model
      expect(modelsRequested).toEqual(['threads', 'contacts', 'files'])

    it "should mark incomplete collections as `busy`", ->
      @worker.start()
      expect(@state).toEqual({
        "contacts": {busy: true}
        "threads": {busy: true}
        "files": {busy: true}
        "calendars": {complete: true}
      })

  describe "when an API request completes", ->
    beforeEach ->
      @worker.start()
      @request = @apiRequests[0]
      @apiRequests = []

    describe "successfully, with models", ->
      it "should request the next page", ->
        models = []
        models.push(new Thread) for i in [0..249]
        @request.requestOptions.success(models)
        expect(@apiRequests.length).toBe(1)
        expect(@apiRequests[0].params).toEqual({limit:250; offset: 250})

    describe "successfully, with fewer models than requested", ->
      beforeEach ->
        models = []
        models.push(new Thread) for i in [0..100]
        @request.requestOptions.success(models)

      it "should not request another page", ->
        @request.requestOptions.success([])
        expect(@apiRequests.length).toBe(0)

      it "should update the state to complete", ->
        @request.requestOptions.success([])
        expect(@state).toEqual({
          "contacts": {busy: true}
          "files": {busy: true}
          "threads": {complete : true}
          "calendars": {complete: true}
        })

    describe "successfully, with no models", ->
      it "should not request another page", ->
        @request.requestOptions.success([])
        expect(@apiRequests.length).toBe(0)

      it "should update the state to complete", ->
        @request.requestOptions.success([])
        expect(@state).toEqual({
          "contacts": {busy: true}
          "files": {busy: true}
          "threads": {complete : true}
          "calendars": {complete: true}
        })

    describe "with an error", ->
      it "should log the error to the state", ->
        err = new Error("Oh no a network error")
        @request.requestOptions.error(err)
        expect(@state).toEqual({
          "contacts": {busy: true}
          "files": {busy: true}
          "threads": {busy: false, error: err.toString()}
          "calendars": {complete: true}
        })

      it "should not request another page", ->
        @request.requestOptions.error(new Error("Oh no a network error"))
        expect(@apiRequests.length).toBe(0)

  describe "cleanup", ->
    it "should termiate the long polling connection", ->
      spyOn(@connection, 'end')
      @worker.cleanup()
      expect(@connection.end).toHaveBeenCalled()

