//
//  AstroFileUtilsTests.swift
//  Astro
//
//  Created by Karl Schmidt on 2015-07-22.
//  Copyright (c) 2015 Mobify Research & Development Inc. All rights reserved.
//

import XCTest
@testable import Astro

// Our testing-purposes network delegate that will trust our self-signed certificate
open class AstroFileUtilsTestNetworkDelegate: NSObject, URLSessionDelegate {
    open func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        var credential: URLCredential? = nil
        if let trust = challenge.protectionSpace.serverTrust {
            credential = URLCredential(trust: trust)
        }
        completionHandler(
            .useCredential,
            credential)
    }
}

class AstroFileUtilsTests: AstroTestCase {
    var respond: CapturedRpcMethodResult!

    override func setUp() {
        XCTAssertTrue(ensureFixtureServer(), "Fixture server must be running")

        respond = CapturedRpcMethodResult()
    }

    func respondFulfillBlock(_ expectation: XCTestExpectation, respond: CapturedRpcMethodResult) -> ((_ result: RPCMethodResult) -> Void) {
        return {(result: RPCMethodResult) in
            respond.callback(result)
            expectation.fulfill()
        }
    }

    func testASyncCaseSuccess(_ url: String) {
        let expectation = self.expectation(description: "imageForURLString should have completed")

        AstroFileUtils.image(path: url, respond: respond.callback, success: { (image: UIImage) in
            XCTAssertNotNil(image, "Image via network call must be loaded")
            expectation.fulfill()
        })

        waitForExpectations(timeout: 1.5, handler: { _ in
            XCTAssertFalse(self.respond.hasError, "There should be no error in respond")
        })
    }

    func testASyncCaseFail(_ url: String) {
        let expectation = self.expectation(description: "imageForURLString should have completed")

        AstroFileUtils.image(path: url, respond: respondFulfillBlock(expectation, respond: respond), success: { (_) in
            XCTFail("Should not be successful")
        })

        waitForExpectations(timeout: 1.5, handler: { _ in
            XCTAssertTrue(self.respond.hasError, "Error should be set")
        })
    }

    func testLoadLocalImage() {
        let image = AstroFileUtils.image(filePath: "file:///astro-logo.png", respond: respond.callback)
        XCTAssertNotNil(image, "astro-logo.png must be loaded")
        XCTAssertFalse(respond.hasError, "Error should not be set")
    }

    func testLoadLocalImageFail() {
        let image = AstroFileUtils.image(filePath: "file:///fake.png", respond: respond.callback)
        XCTAssertNil(image, "fake.png must not be loaded")
        XCTAssertTrue(respond.hasError, "Error should be set")
    }

    func testLoadLocalImageEmptyFail() {
        let image = AstroFileUtils.image(filePath: "", respond: respond.callback)
        XCTAssertNil(image, "No image should be loaded")
        XCTAssertTrue(respond.hasError, "Error should be set")
    }

    func testLoadRemoteImage() {
        testASyncCaseSuccess("http://localhost:8000/image_one.png")
    }

    func testLoadRemoteImageSecure() {
        let originalSession = AstroWebUtils.sharedNetworkSession

        // Inject our naive network delegate so we can test SSL against our fixture server
        let trustingNetworkDelegate = AstroFileUtilsTestNetworkDelegate()
        AstroWebUtils.sharedNetworkSession = URLSession(configuration: URLSessionConfiguration.default, delegate: trustingNetworkDelegate, delegateQueue: nil)
        let externalImageToTest = "https://localhost:8444/image_one.png"
        testASyncCaseSuccess(externalImageToTest)

        AstroWebUtils.sharedNetworkSession = originalSession // Restore original state
    }

    func testLoadRemoteImageFail() {
        testASyncCaseFail("http://localhost:8000/non-existent.png")
    }

    func testLoadRemoteImageSecureFail() {
        testASyncCaseFail("https://localhost:8000/image_one.png")
    }

    func testLoadLocalImageAsync() {
        testASyncCaseSuccess("file:///astro-logo.png")
    }

    func testLoadLocalImageAsyncFail() {
        testASyncCaseFail("file:///fake.png")
    }

    func testLoadRemoteImageEmptyFail() {
        testASyncCaseFail("")
    }
}
