package solutions.tretter.mindmachine.session

import org.junit.Assert.*
import org.junit.Test

/**
 * Unit tests for two-finger pinch-to-zoom gesture handling logic.
 * These tests verify that pinch-to-zoom is detected and applied correctly without requiring a device or emulator.
 */
class PinchZoomGestureTest {

    @Test
    fun `detects pinch-in gesture from zoom factor greater than 1`() {
        // Simulating two fingers moving closer together
        // In real Compose, calculateZoom() returns the zoom factor based on pointer distance change
        // A factor > 1 means zooming in (fingers closer)
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 600f)
        val zoomFactor = 2.0f // Pinch in 2x

        val result = viewport.applyZoomPan(
            zoomChange = zoomFactor, // > 1 means pinch in (zoom in)
            panPx = 0f,
            widthPx = 400f,
            centroidPx = 200f
        )

        // Zooming in should reduce seconds per screen
        assertTrue("Zooming in should reduce seconds per screen", result.secondsPerScreen < viewport.secondsPerScreen)
        assertEquals(300f, result.secondsPerScreen, 1f)
    }

    @Test
    fun `detects pinch-out gesture from zoom factor less than 1`() {
        // Simulating two fingers moving apart
        // A factor < 1 means zooming out (fingers farther)
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 300f)
        val zoomFactor = 0.5f // Pinch out 2x (zoom out)

        val result = viewport.applyZoomPan(
            zoomChange = zoomFactor, // < 1 means pinch out (zoom out)
            panPx = 0f,
            widthPx = 400f,
            centroidPx = 200f
        )

        // Zooming out should increase seconds per screen
        assertTrue("Zooming out should increase seconds per screen", result.secondsPerScreen > viewport.secondsPerScreen)
        assertEquals(600f, result.secondsPerScreen, 1f)
    }

    @Test
    fun `pinch-zoom maintains focus on centroid point`() {
        val viewport = TimelineViewport(startSec = 100f, secondsPerScreen = 600f)
        val widthPx = 400f
        val centroidPx = 250f // Some point on screen

        // Calculate time at centroid before zoom
        val timeAtCentroidBefore = viewport.xToTimeSec(centroidPx, widthPx)

        // Apply pinch zoom
        val result = viewport.applyZoomPan(
            zoomChange = 1.5f,
            panPx = 0f,
            widthPx = widthPx,
            centroidPx = centroidPx
        )

        // Calculate time at centroid after zoom
        val timeAtCentroidAfter = result.xToTimeSec(centroidPx, widthPx)

        // The time at the centroid should remain approximately the same
        assertEquals("Centroid time should stay same during pinch-zoom", timeAtCentroidBefore, timeAtCentroidAfter, 1f)
    }

    @Test
    fun `pinch-zoom with simultaneous pan handles both transformations`() {
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 600f)
        val widthPx = 400f

        // Apply both zoom and pan simultaneously
        val result = viewport.applyZoomPan(
            zoomChange = 2.0f, // Zoom in 2x
            panPx = 50f,       // Pan right by 50px
            widthPx = widthPx,
            centroidPx = 200f
        )

        // Should have applied both transformations
        assertEquals(300f, result.secondsPerScreen, 1f) // Zoomed in
        assertTrue("Viewport should have shifted due to pan", result.startSec > 0f)
    }

    @Test
    fun `zoom factor of 1 (no pinch) results in no zoom change`() {
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 600f)
        val widthPx = 400f

        val result = viewport.applyZoomPan(
            zoomChange = 1.0f, // No zoom
            panPx = 0f,
            widthPx = widthPx,
            centroidPx = 200f
        )

        // Seconds per screen should remain unchanged
        assertEquals(viewport.secondsPerScreen, result.secondsPerScreen, 0.1f)
    }

    @Test
    fun `extreme pinch-zoom values are clamped to valid range`() {
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 600f)
        val widthPx = 400f

        // Extreme pinch in (zoom in 1000x)
        val resultIn = viewport.applyZoomPan(
            zoomChange = 1000f,
            panPx = 0f,
            widthPx = widthPx,
            centroidPx = 200f
        )
        // Should be clamped to minimum
        assertEquals(30f, resultIn.secondsPerScreen, 1f)

        // Extreme pinch out (zoom out 0.001x)
        val resultOut = viewport.applyZoomPan(
            zoomChange = 0.001f,
            panPx = 0f,
            widthPx = widthPx,
            centroidPx = 200f
        )
        // Should be clamped to maximum
        assertEquals(86400f, resultOut.secondsPerScreen, 1f)
    }

    @Test
    fun `pinch-zoom from edge maintains view on that edge`() {
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 600f)
        val widthPx = 400f

        // Pinch-zoom from left edge (centroid at 0px)
        val result = viewport.applyZoomPan(
            zoomChange = 2.0f,
            panPx = 0f,
            widthPx = widthPx,
            centroidPx = 0f
        )

        // After zoom, time at left edge should still be 0 (or close to it)
        val leftEdgeTime = result.xToTimeSec(0f, widthPx)
        assertTrue("Left edge time should stay near 0 when pinching from edge", leftEdgeTime < 10f)
    }

    @Test
    fun `viewport maintains minimum visible duration constraint`() {
        // Minimum seconds per screen should be 30
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 30f)
        val widthPx = 400f

        // Try to zoom in further
        val result = viewport.applyZoomPan(
            zoomChange = 2.0f,
            panPx = 0f,
            widthPx = widthPx,
            centroidPx = 200f
        )

        // Should be clamped to minimum
        assertEquals(30f, result.secondsPerScreen, 0.1f)
    }

    @Test
    fun `viewport maintains maximum visible duration constraint`() {
        // Maximum seconds per screen should be 86400 (24 hours)
        val viewport = TimelineViewport(startSec = 0f, secondsPerScreen = 86400f)
        val widthPx = 400f

        // Try to zoom out further
        val result = viewport.applyZoomPan(
            zoomChange = 0.5f,
            panPx = 0f,
            widthPx = widthPx,
            centroidPx = 200f
        )

        // Should be clamped to maximum
        assertEquals(86400f, result.secondsPerScreen, 0.1f)
    }
}
