import pytest
from pytest import approx
from rich.console import Console
from rich.text import Text

from textual.app import App, ComposeResult
from textual.color import Gradient
from textual.css.query import NoMatches
from textual.renderables.bar import _apply_gradient
from textual.widget import Widget
from textual.widgets import ProgressBar


async def test_initial_status():
    pb = ProgressBar()
    assert pb.total is None
    assert pb.progress == 0
    assert pb.percentage is None

    pb = ProgressBar(total=100)
    assert pb.total == 100
    assert pb.progress == 0
    assert pb.percentage == 0


async def test_advance():
    pb = ProgressBar(total=100)

    pb.advance(10)
    assert pb.progress == 10
    assert pb.percentage == approx(0.1)

    pb.advance(42)
    assert pb.progress == 52
    assert pb.percentage == approx(0.52)

    pb.advance(0.0625)
    assert pb.progress == 52.0625
    assert pb.percentage == approx(0.520625)


async def test_advance_backwards():
    pb = ProgressBar(total=100)

    pb.progress = 50

    pb.advance(-10)
    assert pb.progress == 40


async def test_progress_overflow():
    pb = ProgressBar(total=100)

    pb.advance(999_999)
    assert pb.percentage == 1

    pb.update(total=50)
    assert pb.percentage == 1


async def test_progress_underflow():
    pb = ProgressBar(total=100)

    pb.advance(-999_999)
    assert pb.percentage == 0


async def test_non_negative_total():
    pb = ProgressBar(total=-100)
    assert pb.total == 0


async def test_update_total():
    pb = ProgressBar()

    pb.update(total=100)
    assert pb.total == 100

    pb.update(total=1000)
    assert pb.total == 1000

    pb.update(total=None)
    assert pb.total is None

    pb.update(total=100)
    assert pb.total == 100


async def test_update_progress():
    pb = ProgressBar(total=100)

    pb.update(progress=10)
    assert pb.progress == 10

    pb.update(progress=73)
    assert pb.progress == 73

    pb.update(progress=40)
    assert pb.progress == 40


async def test_update_advance():
    pb = ProgressBar(total=100)

    pb.update(advance=10)
    assert pb.progress == 10

    pb.update(advance=10)
    assert pb.progress == 20

    pb.update(advance=10)
    assert pb.progress == 30


async def test_update():
    pb = ProgressBar()

    pb.update(total=100, progress=30, advance=20)
    assert pb.total == 100
    assert pb.progress == 50


async def test_go_back_to_indeterminate():
    pb = ProgressBar()

    pb.total = 100
    assert pb.percentage == 0
    pb.total = None
    assert pb.percentage is None


@pytest.mark.parametrize(
    ["show_bar", "show_percentage", "show_eta"],
    [
        (True, True, True),
        (True, True, False),
        (True, False, True),
        (True, False, False),
        (False, True, True),
        (False, True, False),
        (False, False, True),
        (False, False, False),
    ],
)
async def test_show_sub_widgets(show_bar: bool, show_percentage: bool, show_eta: bool):
    class PBApp(App[None]):
        def compose(self):
            self.pb = ProgressBar(
                show_bar=show_bar, show_percentage=show_percentage, show_eta=show_eta
            )
            yield self.pb

    app = PBApp()

    async with app.run_test():
        if show_bar:
            bar = app.pb.query_one("#bar")
            assert isinstance(bar, Widget)
        else:
            with pytest.raises(NoMatches):
                app.pb.query_one("#bar")

        if show_percentage:
            percentage = app.pb.query_one("#percentage")
            assert isinstance(percentage, Widget)
        else:
            with pytest.raises(NoMatches):
                app.pb.query_one("#percentage")

        if show_eta:
            eta = app.pb.query_one("#eta")
            assert isinstance(eta, Widget)
        else:
            with pytest.raises(NoMatches):
                app.pb.query_one("#eta")


def test_apply_gradient():
    text = Text("foo")
    gradient = Gradient.from_colors("red", "blue")
    _apply_gradient(text, gradient, 1)
    console = Console()
    assert text.get_style_at_offset(console, 0).color.triplet == (255, 0, 0)


async def test_progress_bar_width_1fr(snap_compare):
    """Regression test for https://github.com/Textualize/textual/issues/6127

    Just shouldn't crash...
    """

    class WideBarApp(App[None]):

        CSS = """
        ProgressBar Bar {
            width: 1fr;
        }
        """

        def compose(self) -> ComposeResult:
            yield ProgressBar()

    app = WideBarApp()
    async with app.run_test() as pilot:
        await pilot.pause()
