package dev.adriankuta.flights.model.repository.usecases

import dev.adriankuta.flights.domain.search.entities.SearchOptions
import dev.adriankuta.flights.domain.types.Airport
import dev.adriankuta.flights.domain.types.Flight
import dev.adriankuta.flights.domain.types.MacCity
import dev.adriankuta.flights.domain.types.RegularFare
import dev.adriankuta.flights.domain.types.Segment
import dev.adriankuta.flights.domain.types.Trip
import dev.adriankuta.flights.domain.types.TripDate
import dev.adriankuta.flights.domain.types.TripFare
import dev.adriankuta.flights.domain.types.TripFlight
import dev.adriankuta.model.data.api.FlightService
import dev.adriankuta.model.data.api.entities.FlightResponse
import io.mockk.coEvery
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import kotlinx.datetime.LocalDate
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test

class GetFlightsSearchContentUseCaseImplTest {

    private lateinit var flightService: FlightService
    private lateinit var useCase: GetFlightsSearchContentUseCaseImpl

    @Before
    fun setup() {
        flightService = mockk()
        useCase = GetFlightsSearchContentUseCaseImpl(flightService)
    }

    @Test
    @Suppress("LongMethod")
    fun `invoke returns mapped flight when service returns valid response`() = runTest {
        // Given
        val searchOptions = SearchOptions(
            origin = Airport.Departure(
                code = "WAW",
                name = "Warsaw",
                macCity = MacCity(macCode = "WAW"),
            ),
            destination = Airport.Arrival(
                code = "LDN",
                name = "London",
                macCity = MacCity(macCode = "LDN"),
            ),
            date = LocalDate(2023, 10, 15),
            adults = 1,
            teens = 0,
            children = 0,
        )

        val flightResponse = FlightResponse(
            currency = "EUR",
            currPrecision = 2,
            trips = listOf(
                dev.adriankuta.model.data.api.entities.Trip(
                    dates = listOf(
                        dev.adriankuta.model.data.api.entities.TripDate(
                            dateOut = "2023-10-15T00:00:00",
                            flights = listOf(
                                dev.adriankuta.model.data.api.entities.TripFlight(
                                    faresLeft = 10,
                                    regularFare = dev.adriankuta.model.data.api.entities.RegularFare(
                                        fares = listOf(
                                            dev.adriankuta.model.data.api.entities.TripFare(
                                                type = "ADT",
                                                amount = 99.99,
                                                count = 1,
                                            ),
                                        ),
                                    ),
                                    flightNumber = "FR1234",
                                    dateTimes = listOf(
                                        "2023-10-15T10:00:00",
                                        "2023-10-15T12:00:00",
                                    ),
                                    duration = "2:00",
                                    segments = listOf(
                                        dev.adriankuta.model.data.api.entities.Segment(
                                            origin = "WAW",
                                            destination = "LDN",
                                            flightNumber = "FR1234",
                                            dateTimes = listOf(
                                                "2023-10-15T10:00:00",
                                                "2023-10-15T12:00:00",
                                            ),
                                            duration = "2:00",
                                        ),
                                    ),
                                    operatedBy = "Ryanair",
                                ),
                            ),
                        ),
                    ),
                    origin = "WAW",
                    destination = "LDN",
                ),
            ),
        )

        coEvery {
            flightService.getFlights(
                date = "2023-10-15",
                origin = "WAW",
                destination = "LDN",
                adult = 1,
                teen = 0,
                child = 0,
            )
        } returns flightResponse

        // When
        val result = useCase.invoke(searchOptions)

        // Then
        val expectedFlight = Flight(
            currency = "EUR",
            currPrecision = 2,
            trips = listOf(
                Trip(
                    dates = listOf(
                        TripDate(
                            dateOut = LocalDate(2023, 10, 15),
                            flights = listOf(
                                TripFlight(
                                    faresLeft = 10,
                                    regularFare = RegularFare(
                                        fares = listOf(
                                            TripFare(
                                                type = "ADT",
                                                amount = 99.99,
                                                count = 1,
                                            ),
                                        ),
                                    ),
                                    flightNumber = "FR1234",
                                    dateTimes = listOf(
                                        "2023-10-15T10:00:00",
                                        "2023-10-15T12:00:00",
                                    ),
                                    duration = "2:00",
                                    segments = listOf(
                                        Segment(
                                            origin = "WAW",
                                            destination = "LDN",
                                            flightNumber = "FR1234",
                                            dateTimes = listOf(
                                                "2023-10-15T10:00:00",
                                                "2023-10-15T12:00:00",
                                            ),
                                            duration = "2:00",
                                        ),
                                    ),
                                    operatedBy = "Ryanair",
                                ),
                            ),
                        ),
                    ),
                    origin = "WAW",
                    destination = "LDN",
                ),
            ),
        )

        assertEquals(expectedFlight, result)
    }

    @Test
    fun `invoke handles null values in response correctly`() = runTest {
        // Given
        val searchOptions = SearchOptions(
            origin = Airport.Departure(
                code = "WAW",
                name = "Warsaw",
                macCity = MacCity(macCode = "WAW"),
            ),
            destination = Airport.Arrival(
                code = "LDN",
                name = "London",
                macCity = MacCity(macCode = "LDN"),
            ),
            date = LocalDate(2023, 10, 15),
            adults = 1,
            teens = 0,
            children = 0,
        )

        val flightResponse = FlightResponse(
            currency = null,
            currPrecision = null,
            trips = null,
        )

        coEvery {
            flightService.getFlights(
                date = "2023-10-15",
                origin = "WAW",
                destination = "LDN",
                adult = 1,
                teen = 0,
                child = 0,
            )
        } returns flightResponse

        // When
        val result = useCase.invoke(searchOptions)

        // Then
        val expectedFlight = Flight(
            currency = "",
            currPrecision = 0,
            trips = emptyList(),
        )

        assertEquals(expectedFlight, result)
    }
}
