from time import perf_counter

from textual.app import App, ComposeResult
from textual.widgets import Static


class AnimApp(App):
    CSS = """
    #foo {
        height: 1;
    }

    """

    def compose(self) -> ComposeResult:
        yield Static("foo", id="foo")


async def test_animate_height() -> None:
    """Test animating styles.height works."""

    # Styles.height is a scalar, which makes it more complicated to animate

    app = AnimApp()

    async with app.run_test() as pilot:
        static = app.query_one(Static)
        assert static.size.height == 1
        assert static.styles.height.value == 1
        static.styles.animate("height", 100, duration=0.5, easing="linear")
        start = perf_counter()

        # Wait for the animation to finished
        await pilot.wait_for_animation()
        elapsed = perf_counter() - start
        # Check that the full time has elapsed
        assert elapsed >= 0.5
        # Check the height reached the maximum
        assert static.styles.height.value == 100


async def test_scheduling_animation() -> None:
    """Test that scheduling an animation works."""

    app = AnimApp()
    delay = 0.1

    async with app.run_test() as pilot:
        styles = app.query_one(Static).styles
        styles.background = "black"

        styles.animate("background", "white", delay=delay, duration=0)

        await pilot.pause(0.9 * delay)
        assert styles.background.rgb == (0, 0, 0)  # Still black

        await pilot.wait_for_scheduled_animations()
        assert styles.background.rgb == (255, 255, 255)


async def test_wait_for_current_animations() -> None:
    """Test that we can wait only for the current animations taking place."""

    app = AnimApp()

    delay = 10

    async with app.run_test() as pilot:
        styles = app.query_one(Static).styles
        styles.animate("height", 100, duration=0.1)
        start = perf_counter()
        styles.animate("height", 200, duration=0.1, delay=delay)

        # Wait for the first animation to finish
        await pilot.wait_for_animation()
        elapsed = perf_counter() - start
        assert elapsed < (delay / 2)


async def test_wait_for_current_and_scheduled_animations() -> None:
    """Test that we can wait for current and scheduled animations."""

    app = AnimApp()

    async with app.run_test() as pilot:
        styles = app.query_one(Static).styles

        start = perf_counter()
        styles.animate("height", 50, duration=0.01)
        styles.animate("background", "black", duration=0.01, delay=0.05)

        await pilot.wait_for_scheduled_animations()
        elapsed = perf_counter() - start
        assert elapsed >= 0.06
        assert styles.background.rgb == (0, 0, 0)


async def test_reverse_animations() -> None:
    """Test that you can create reverse animations.

    Regression test for #1372 https://github.com/Textualize/textual/issues/1372
    """

    app = AnimApp()

    async with app.run_test() as pilot:
        static = app.query_one(Static)
        styles = static.styles

        # Starting point.
        styles.background = "black"
        assert styles.background.rgb == (0, 0, 0)

        # First, make sure we can go from black to white and back, step by step.
        styles.animate("background", "white", duration=0.01)
        await pilot.wait_for_animation()
        assert styles.background.rgb == (255, 255, 255)

        styles.animate("background", "black", duration=0.01)
        await pilot.wait_for_animation()
        assert styles.background.rgb == (0, 0, 0)

        # Now, the actual test is to make sure we go back to black if creating both at once.
        styles.animate("background", "white", duration=0.01)
        styles.animate("background", "black", duration=0.01)
        await pilot.wait_for_animation()
        assert styles.background.rgb == (0, 0, 0)


async def test_schedule_reverse_animations() -> None:
    """Test that you can schedule reverse animations.

    Regression test for #1372 https://github.com/Textualize/textual/issues/1372
    """

    app = AnimApp()

    async with app.run_test() as pilot:
        static = app.query_one(Static)
        styles = static.styles

        # Starting point.
        styles.background = "black"
        assert styles.background.rgb == (0, 0, 0)

        # First, make sure we can go from black to white and back, step by step.
        styles.animate("background", "white", delay=0.01, duration=0.01)
        await pilot.wait_for_scheduled_animations()
        assert styles.background.rgb == (255, 255, 255)

        styles.animate("background", "black", delay=0.01, duration=0.01)
        await pilot.wait_for_scheduled_animations()
        assert styles.background.rgb == (0, 0, 0)

        # Now, the actual test is to make sure we go back to black if scheduling both at once.
        styles.animate("background", "white", delay=0.05, duration=0.01)
        await pilot.pause()
        styles.animate("background", "black", delay=0.05, duration=0.01)
        await pilot.wait_for_scheduled_animations()
        assert styles.background.rgb == (0, 0, 0)
