//
//  LocationsClientTests.swift
//  LocationsTests
//
//  Created by Javier Cicchelli on 10/04/2023.
//  Copyright © 2023 Röck+Cöde. All rights reserved.
//

import APICore
import XCTest

@testable import Locations

final class LocationsClientTests: XCTestCase {

    // MARK: Properties
    
    private let makeURLRequest = MakeURLRequestUseCase()
    private let sessionConfiguration = {
        let configuration = URLSessionConfiguration.default

        configuration.protocolClasses = [MockURLProtocol.self]
        
        return configuration
    }()

    private var client: LocationsClient!
    private var url: URL!
    private var data: Data!

    // MARK: Setup
    
    override func setUp() async throws {
        client = .init(configuration: sessionConfiguration)
    }
    
    override func tearDown() async throws {
        client = nil
    }
    
    // MARK: Tests
    
    func test_request_withGetLocationsEndpoint_forLocations_whenResponseOK() async throws {
        // GIVEN
        let endpoint = GetLocationsEndpoint()
        
        url = try makeURLRequest(endpoint: endpoint).url
        data = .Responses.locations
        
        MockURLProtocol.mockData[url] = MockURLResponse(
            status: 200,
            headers: [:],
            data: data
        )
        
        // WHEN
        let result = try await client.request(endpoint: endpoint, for: Locations.self)
        
        // THEN
        XCTAssertEqual(result, Locations(locations: [
            .init(
                name: "Amsterdam",
                latitude: 52.3547498,
                longitude: 4.8339215
            ),
            .init(
                name: "Mumbai",
                latitude: 19.0823998,
                longitude: 72.8111468
            ),
            .init(
                name: "Copenhagen",
                latitude: 55.6713442,
                longitude: 12.523785
            ),
            .init(
                latitude: 40.4380638,
                longitude: -3.7495758
            )
        ]))
    }
    
    func test_request_withGetLocationsEndpoint_forLocations_whenResponseClientError() async throws {
        // GIVEN
        let endpoint = GetLocationsEndpoint()
        
        url = try makeURLRequest(endpoint: endpoint).url
        data = .Responses.locations
        
        MockURLProtocol.mockData[url] = MockURLResponse(
            status: 404,
            headers: [:],
            data: data
        )
        
        // WHEN & THEN
        do {
            _ = try await client.request(endpoint: endpoint, for: Locations.self)
        } catch LocationsClientError.statusErrorClient {
            XCTAssertTrue(true)
        } catch {
            XCTAssertTrue(false)
        }
    }
    
    func test_request_withGetLocationsEndpoint_forLocations_whenResponseServerError() async throws {
        // GIVEN
        let endpoint = GetLocationsEndpoint()
        
        url = try makeURLRequest(endpoint: endpoint).url
        data = .Responses.locations
        
        MockURLProtocol.mockData[url] = MockURLResponse(
            status: 500,
            headers: [:],
            data: data
        )
        
        // WHEN & THEN
        do {
            _ = try await client.request(endpoint: endpoint, for: Locations.self)
        } catch LocationsClientError.statusErrorServer {
            XCTAssertTrue(true)
        } catch {
            XCTAssertTrue(false)
        }
    }
    
    func test_request_withGetLocationsEndpoint_forLocations_whenResponseUnexpectedError() async throws {
        // GIVEN
        let endpoint = GetLocationsEndpoint()
        
        url = try makeURLRequest(endpoint: endpoint).url
        data = .Responses.locations
        
        MockURLProtocol.mockData[url] = MockURLResponse(
            status: 302,
            headers: [:],
            data: data
        )
        
        // WHEN & THEN
        do {
            _ = try await client.request(endpoint: endpoint, for: Locations.self)
        } catch LocationsClientError.statusErrorUnexpected {
            XCTAssertTrue(true)
        } catch {
            XCTAssertTrue(false)
        }
    }

}
