//
//  AstroWorkerTests.swift
//  Astro
//
//  Created by Jeremy Wiebe on 2015-04-27.
//  Copyright (c) 2015 Mobify Research & Development Inc. All rights reserved.
//

import XCTest
@testable import Astro

class TestWebBridge: WebBridge {
    var viewController = UIViewController()
    weak var webBridgeDelegate: WebBridgeDelegate?
    var sentMessage: BridgeMessage?
    var addedScriptUrl: String?
    var addedScriptUrlCallCount = 0

    func sendMessage(to address: MessageAddress, data dataJson: String) {
        if let data = JSON.deserialize(dataJson) {
            sentMessage = BridgeMessage(address: address, jsonObject: data)
        }
    }

    func load(_ request: URLRequest) {
        abort()
    }

    func addScript(atURL url: String) {
        addedScriptUrlCallCount += 1
        addedScriptUrl = url
    }
}

class AstroWorkerTests: AstroTestCase {

    func testReceiveMessageWithJsRpcRequestForwardsToBridge() {
        expectAssertion { expectation in
            let bridge = TestWebBridge()
            let messageBus = MessageBus()
            let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

            let response = TestMessageFactory.createJsRpcRequest("destination", id: 3)
            worker.receive(response)

            if let sentMessage = bridge.sentMessage {
                XCTAssertEqual(sentMessage, BridgeMessage(address: "destination", jsonObject: ["id": 3, "isJsRpc": true, "senderAddress": "source", "payload": response.payload]))
                expectation.fulfill()
            }
        }
    }

    func testReceiveMessageWithRpcResponseForwardsToBridge() {
        expectAssertion { expectation in
            let bridge = TestWebBridge()
            let messageBus = MessageBus()
            let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

            let response = TestMessageFactory.createRpcResponse("destination", id: 3)
            worker.receive(response)

            if let sentMessage = bridge.sentMessage {
                XCTAssertEqual(sentMessage, BridgeMessage(address: "destination", jsonObject: ["id": 3, "payload": response.payload]))
                expectation.fulfill()
            }
        }
    }

    func testReceiveMessageWithEventMessageForwardsToBridge() {
        expectAssertion { expectation in
            let bridge = TestWebBridge()
            let messageBus = MessageBus()
            let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

            let eventMessage = TestMessageFactory.createEventMessage("destination")
            worker.receive(eventMessage)

            if let sentMessage = bridge.sentMessage {
                XCTAssertEqual(sentMessage, BridgeMessage(address: "destination", jsonObject: ["payload": eventMessage.payload]))
                expectation.fulfill()
            }
        }
    }

    func testReceiveMessageFromBridgeSendsMessageToBus() {
        let bridge = TestWebBridge()
        let messageBus = MessageBus()
        let receiver = TestReceiver(address: "destination")

        messageBus.register(receiver)

        let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

        worker.receiveMessageFromBridge(TestMessageFactory.createEventMessage("destination").toBridgeMessage())

        XCTAssertNotNil(receiver.receivedMessage)
    }

    func testReceiveMessageFromBridgeToSelfSendsMessageToWorker() {
        expectAssertion { expectation in
            let bridge = TestWebBridge()
            let messageBus = MessageBus()

            let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

            // We cheat a bit here... we listen on the worker's address to detect that
            // the message was sent to the worker's address correctly
            let receiver = TestReceiver(address: "ignored_address")
            messageBus.listen(on: worker.address, receiver: receiver)

            worker.receiveMessageFromBridge(TestMessageFactory.createEventMessage("self").toBridgeMessage())

            if let message = receiver.receivedMessage {
                XCTAssertEqual(message.to, worker.address)
                expectation.fulfill()
            }
        }
    }

    func testAppJsNotLoadedUntilBootIsCalled() {
        let bridge = TestWebBridge()
        let messageBus = MessageBus()
        let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

        XCTAssertNil(bridge.addedScriptUrl)
        worker.boot()

        if let addedScriptUrl = bridge.addedScriptUrl {
            XCTAssertTrue(addedScriptUrl.range(of: "app.js") != nil)
        } else {
            XCTFail("app.js not loaded")
        }
    }

    func testBootCantBeCalledMoreThanOnce() {
        let bridge = TestWebBridge()
        let messageBus = MessageBus()
        let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

        worker.boot()
        worker.boot()

        XCTAssertEqual(1, bridge.addedScriptUrlCallCount)
    }

    func testCanSubscribeToEvents() {
        let bridge = TestWebBridge()
        let messageBus = MessageBus()
        let worker = AstroWorker(appJSURL: URL(string: "app.js")!, messageBus: messageBus, webBridge: bridge)

        worker.subscribeToEvents(from: "FakePluginAddress:0" as MessageAddress) { _ in }

        XCTAssertTrue(messageBus.isListening(toAddress: "FakePluginAddress:0:events", receiver: worker))
    }
}
