//
//  Copyright (c) 2015 Mobify Research & Development Inc. All rights reserved.
//

import XCTest
@testable import Astro

class MockAlertAction: UIAlertAction {
    typealias Handler = ((UIAlertAction) -> Void)
    @objc var handler: Handler?
    @objc var mockTitle: String?
    @objc var mockStyle: UIAlertAction.Style

    @objc convenience init(title: String?, style: UIAlertAction.Style, handler: ((UIAlertAction) -> Void)?) {
        self.init()

        mockTitle = title
        mockStyle = style
        self.handler = handler
    }

    override init() {
        mockStyle = .default

        super.init()
    }
}

class ListSelectPluginTests: AstroTestCase {
    var messageBus: MessageBus!
    var pluginResolver: StubPluginResolver!
    var listSelectPlugin: ListSelectPlugin!

    var items: [JSONObject]!
    var listItems: [ListItem] = []
    var invalidItems: [JSONObject]!

    override func setUp() {
        messageBus = MessageBus()
        pluginResolver = StubPluginResolver()
        listSelectPlugin = ListSelectPlugin(address: "ListSelectPlugin:0", messageBus: messageBus, pluginResolver: pluginResolver, options: nil)

        invalidItems = [["id": "Hello"], ["label": "test"], ["nothing": 3]]
        items = [createListItem("one"), createListItem("two"), createListItem("three"), createListItem("four")]

        for i in items {
            let listItem = listSelectPlugin.parseItem(i) {_ in}
            if let listItem = listItem {
                listItems.append(listItem)
            }
        }
    }

    // Creates a list item with the id and label set to name
    func createListItem(_ name: String) -> JSONObject {
        let item: JSONObject = ["id": name, "label": name]
        return item
    }

    func testSetTitle() {
        let title = "title"
        listSelectPlugin.setTitle(title) {_ in}

        AssertEqual(listSelectPlugin.title, b: title)
    }

    func testParsingItems() {
        for item in items {
            let listItem = listSelectPlugin.parseItem(item) {_ in}
            AssertNotNil(listItem)

            AssertEqual(listItem!.id, b: item["id"] as? String)
            AssertEqual(listItem!.label, b: item["label"] as? String)
        }
    }

    func testParsingInvalidItems() {
        for item in invalidItems {
            let listItem = listSelectPlugin.parseItem(item) {_ in}
            AssertNil(listItem)
        }
    }

    func testAddingInvalidItems() {
        for item in invalidItems {
            listSelectPlugin.addItem(item, respond: CapturedRpcMethodResult.generateFailureCallback { value in
                AssertNotNil(value)
            })
        }
        AssertEqual(listSelectPlugin.list.count, b: 0)
    }

    func testAddingItems() {
        AssertEqual(listSelectPlugin.list.count, b: 0)

        var count = 0

        for i in items {
            listSelectPlugin.addItem(i) {_ in}
            count += 1
            AssertEqual(listSelectPlugin.list.count, b: count)
        }

        AssertEqualArrays(listSelectPlugin.list, b: listItems)
    }

    func testSettingInvalidList() {
        listSelectPlugin.setList(invalidItems, respond: CapturedRpcMethodResult.generateFailureCallback { value in
            AssertNotNil(value)
        })
        AssertEqual(listSelectPlugin.list.count, b: 0)
    }

    func testSettingList() {
        AssertEqual(listSelectPlugin.list.count, b: 0)

        listSelectPlugin.addItem(createListItem("test")) {_ in}

        // Setting the list should overwrite the current list
        listSelectPlugin.setList(items) {_ in}

        AssertEqual(listSelectPlugin.list.count, b: items.count)
        AssertEqualArrays(listSelectPlugin.list, b: listItems)
    }

    func testSelectingItem() {
        listSelectPlugin.actionType = MockAlertAction.self

        listSelectPlugin.setList(items) {_ in}
        let selectedItem = listItems[1]

        expectAssertion { expectation in
            self.listSelectPlugin.setCancelable(false) {_ in}
            self.listSelectPlugin.show(nil, respond: CapturedRpcMethodResult.generateSuccessCallback { value in
                AssertNotNil(value)
                if let json: JSONObject = value as? JSONObject {
                    AssertEqualType(json["cancelled"], b: false)
                    AssertEqualType(json["selected"], b: selectedItem.id)
                } else {
                    XCTFail("Resolved value should be a JSONObject")
                }
                expectation.fulfill()
            })

            AssertEqual(self.listSelectPlugin.listSelectController.actions.count, b: self.items.count)
            let action = self.listSelectPlugin.listSelectController.actions[1] as! MockAlertAction
            action.handler!(action)
        }
    }

    func testCancelling() {
        listSelectPlugin.actionType = MockAlertAction.self

        listSelectPlugin.setList(items) {_ in}

        expectAssertion { expectation in
            self.listSelectPlugin.setCancelable(true) {_ in}
            self.listSelectPlugin.show(nil, respond: CapturedRpcMethodResult.generateSuccessCallback { value in
                AssertNotNil(value)
                if let json: JSONObject = value as? JSONObject {
                    AssertEqualType(json["cancelled"], b: true)
                } else {
                    XCTFail("Resolved value should be a JSONObject")
                }
                expectation.fulfill()
            })

            let action = self.listSelectPlugin.listSelectController.actions.first as! MockAlertAction
            action.handler!(action)
        }

    }
}
