package com.doctate.watch.presentation

import com.google.common.truth.Truth.assertThat
import org.junit.Test

class UiStateReducerTest {

    @Test fun idle_to_requesting_permission_on_record_tap() {
        val next = reduce(UiState.Idle, RecordingEvent.OnRecordTap)
        assertThat(next).isEqualTo(UiState.RequestingPermission)
    }

    @Test fun requesting_permission_to_recording_on_grant() {
        val next = reduce(UiState.RequestingPermission, RecordingEvent.PermissionGranted)
        assertThat(next).isEqualTo(UiState.Recording(elapsedSeconds = 0))
    }

    @Test fun requesting_permission_to_error_on_denial() {
        val next = reduce(UiState.RequestingPermission, RecordingEvent.PermissionDenied)
        assertThat(next).isInstanceOf(UiState.Error::class.java)
        assertThat((next as UiState.Error).transient).isFalse()
    }

    @Test fun recording_updates_elapsed_seconds_on_tick() {
        val next = reduce(UiState.Recording(elapsedSeconds = 0), RecordingEvent.RecordingTick(3))
        assertThat(next).isEqualTo(UiState.Recording(elapsedSeconds = 3))
    }

    @Test fun recording_moves_to_uploading_on_stop_tap() {
        val next = reduce(UiState.Recording(elapsedSeconds = 12), RecordingEvent.OnStopTap)
        assertThat(next).isEqualTo(UiState.Uploading)
    }

    @Test fun recording_moves_to_idle_on_discard_tap() {
        val next = reduce(UiState.Recording(elapsedSeconds = 12), RecordingEvent.OnDiscardTap)
        assertThat(next).isEqualTo(UiState.Idle)
    }

    @Test fun recording_moves_to_uploading_when_finished() {
        val next = reduce(UiState.Recording(elapsedSeconds = 5), RecordingEvent.RecordingFinished)
        assertThat(next).isEqualTo(UiState.Uploading)
    }

    @Test fun recording_to_error_on_recorder_failure() {
        val next = reduce(
            UiState.Recording(elapsedSeconds = 3),
            RecordingEvent.UploadFailed("mic busy", transient = false),
        )
        val err = next as UiState.Error
        assertThat(err.message).isEqualTo("mic busy")
        assertThat(err.transient).isFalse()
    }

    @Test fun uploading_to_success_on_ack() {
        val next = reduce(UiState.Uploading, RecordingEvent.UploadSucceeded("abc-123"))
        assertThat(next).isEqualTo(UiState.Success("abc-123"))
    }

    @Test fun uploading_to_error_on_transient_failure() {
        val next = reduce(UiState.Uploading, RecordingEvent.UploadFailed("timeout", transient = true))
        val err = next as UiState.Error
        assertThat(err.message).isEqualTo("timeout")
        assertThat(err.transient).isTrue()
    }

    @Test fun success_resets_to_idle_on_tap() {
        val next = reduce(UiState.Success("abc-123"), RecordingEvent.OnResetTap)
        assertThat(next).isEqualTo(UiState.Idle)
    }

    @Test fun error_resets_to_idle_on_tap() {
        val next = reduce(UiState.Error("boom", true), RecordingEvent.OnResetTap)
        assertThat(next).isEqualTo(UiState.Idle)
    }

    @Test fun unknown_event_is_noop() {
        val next = reduce(UiState.Idle, RecordingEvent.UploadSucceeded("late ack"))
        assertThat(next).isEqualTo(UiState.Idle)
    }

    @Test fun discard_tap_outside_recording_is_noop() {
        val next = reduce(UiState.Uploading, RecordingEvent.OnDiscardTap)
        assertThat(next).isEqualTo(UiState.Uploading)
    }
}
