package pl.firmatpp.kierowca.ui

import com.google.gson.Gson
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import pl.firmatpp.kierowca.data.model.DriverRouteDto
import pl.firmatpp.kierowca.data.model.RoutePhotoDto
import pl.firmatpp.kierowca.data.upload.RouteActionEntity
import pl.firmatpp.kierowca.data.upload.RouteActionStatus
import pl.firmatpp.kierowca.data.upload.RouteActionType

class DriverRouteProjectionTest {
    @Test
    fun pendingStartProjectsPlannedRouteAsActiveWithLoadingWeight() {
        val projection = projectDriverRoute(
            route = route(status = "ZAPLANOWANA"),
            actions = listOf(action(RouteActionType.Start, RouteActionStatus.Pending, weight = 12.5)),
        )

        assertEquals("W TRAKCIE", projection.route.status)
        assertEquals(12.5, projection.route.loadingWeight)
        assertEquals("active", projection.route.trackingStatus)
        assertTrue(projection.loadingWeightPending)
        assertFalse(projection.unloadingWeightPending)
        assertEquals(RouteLifecycleAction.Finish, routeLifecyclePrimaryAction(projection.route, "2026-06-30", today)?.action)
    }

    @Test
    fun pendingFinishProjectsActiveRouteAsFinishedWithUnloadingWeight() {
        val projection = projectDriverRoute(
            route = route(status = "W TRAKCIE", loadingWeight = 12.5),
            actions = listOf(action(RouteActionType.Finish, RouteActionStatus.Syncing, weight = 11.8, occurredAt = "2026-06-30T09:10:00Z")),
        )

        assertEquals("ZAKOŃCZONA", projection.route.status)
        assertEquals(11.8, projection.route.unloadingWeight)
        assertEquals("finished", projection.route.trackingStatus)
        assertEquals("2026-06-30T09:10:00Z", projection.route.completedAt)
        assertTrue(projection.unloadingWeightPending)
        assertNull(routeLifecyclePrimaryAction(projection.route, "2026-06-30", today))
    }

    @Test
    fun startAndFinishAreAppliedChronologicallySoFinishWins() {
        val projection = projectDriverRoute(
            route = route(status = "ZAPLANOWANA"),
            actions = listOf(
                action(RouteActionType.Finish, RouteActionStatus.Pending, weight = 11.8, createdAt = 2000),
                action(RouteActionType.Start, RouteActionStatus.Pending, weight = 12.5, createdAt = 1000),
            ),
        )

        assertEquals("ZAKOŃCZONA", projection.route.status)
        assertEquals(12.5, projection.route.loadingWeight)
        assertEquals(11.8, projection.route.unloadingWeight)
        assertEquals("finished", projection.route.trackingStatus)
    }

    @Test
    fun confirmedFinishStillProjectsPlannedRouteAsFinishedWithoutSyncCallout() {
        val projection = projectDriverRoute(
            route = route(status = "ZAPLANOWANA"),
            actions = listOf(
                action(RouteActionType.Finish, RouteActionStatus.Confirmed, weight = 11.8, createdAt = 2000),
            ),
        )

        assertEquals("ZAKOŃCZONA", projection.route.status)
        assertEquals(11.8, projection.route.unloadingWeight)
        assertEquals("finished", projection.route.trackingStatus)
        assertFalse(projection.unloadingWeightPending)
        assertEquals(emptyList<RouteActionEntity>(), projection.visibleActions)
    }

    @Test
    fun routeProjectionNormalizesNullPhotosFromCachedJsonBeforeCopy() {
        val route = Gson().fromJson(
            """
            {
              "id": "1",
              "startsAt": "",
              "originName": "Baza",
              "destinationName": "Instalacja",
              "contractorName": "TPP",
              "contractName": "Kontrakt",
              "contractCode": "TPP-1",
              "relationLabel": "Baza -> Instalacja",
              "status": "ZAPLANOWANA",
              "distanceKm": 1.0,
              "notes": null,
              "truck": null,
              "photos": null
            }
            """.trimIndent(),
            DriverRouteDto::class.java,
        )

        val projection = projectDriverRoute(
            route = route,
            actions = listOf(action(RouteActionType.Start, RouteActionStatus.Confirmed, weight = 12.5)),
        )

        assertEquals("W TRAKCIE", projection.route.status)
        assertEquals(emptyList<RoutePhotoDto>(), projection.route.photos)
    }

    @Test
    fun conflictKeepsLocalRouteValuesAndMarksStageAsAttention() {
        val projection = projectDriverRoute(
            route = route(status = "W TRAKCIE", loadingWeight = 12.5),
            actions = listOf(action(RouteActionType.Finish, RouteActionStatus.FailedConflict, weight = 11.8)),
        )
        val steps = routeFlowSteps(projection.route, projection.visibleActions)
        val callout = routeSyncCallout(projection.visibleActions)

        assertEquals("ZAKOŃCZONA", projection.route.status)
        assertEquals(11.8, projection.route.unloadingWeight)
        assertEquals(RouteFlowStepState.NeedsAttention, steps[3].state)
        assertEquals("Kurs wymaga obsługi. Dane wpisane w telefonie zostały zachowane.", callout?.text)
        assertTrue(callout?.isConflict ?: false)
    }

    @Test
    fun failedPermanentIsVisibleAndNeedsAttention() {
        val projection = projectDriverRoute(
            route = route(status = "ZAPLANOWANA"),
            actions = listOf(action(RouteActionType.Start, RouteActionStatus.FailedPermanent, weight = 12.5)),
        )
        val steps = routeFlowSteps(projection.route, projection.visibleActions)

        assertEquals("W TRAKCIE", projection.route.status)
        assertEquals(12.5, projection.route.loadingWeight)
        assertEquals(RouteFlowStepState.NeedsAttention, steps[1].state)
    }

    @Test
    fun noLocalActionsLeavesRouteUnchanged() {
        val route = route(status = "ZAPLANOWANA")
        val projection = projectDriverRoute(route, emptyList())

        assertEquals(route, projection.route)
        assertFalse(projection.loadingWeightPending)
        assertFalse(projection.unloadingWeightPending)
        assertEquals(emptyList<RouteActionEntity>(), projection.visibleActions)
    }

    private fun route(
        status: String,
        loadingWeight: Double? = null,
        unloadingWeight: Double? = null,
    ): DriverRouteDto =
        DriverRouteDto(
            id = "1",
            startsAt = "",
            originName = "Baza",
            destinationName = "Instalacja",
            contractorName = "TPP",
            contractName = "Kontrakt",
            contractCode = "TPP-1",
            relationLabel = "Baza -> Instalacja",
            status = status,
            loadingWeight = loadingWeight,
            unloadingWeight = unloadingWeight,
            distanceKm = 1.0,
            notes = null,
            truck = null,
        )

    private fun action(
        type: String,
        status: String,
        weight: Double,
        occurredAt: String = "2026-06-30T07:40:00Z",
        createdAt: Long = 1000,
    ): RouteActionEntity =
        RouteActionEntity(
            clientActionId = "action-$type-$status-$createdAt",
            routeId = "1",
            action = type,
            weight = weight,
            occurredAt = occurredAt,
            photoClientRequestIdsJson = "[]",
            status = status,
            createdAtEpochMillis = createdAt,
        )

    private companion object {
        val today = java.time.LocalDate.parse("2026-06-30")
    }
}
