import asyncio
import time
from typing import Any
from uuid import UUID

from alloy_runtime_types.dtos.executions import GetExecutionResponse
from cli.commands.executions.get.transcript_renderer import (
    render_execution_snapshot,
)
from cli.infrastructure.output import OutputFormat, OutputService
from rich.live import Live
from rich.text import Text

_LIVE_POLL_INTERVAL_SECONDS = 3.0


def present_execution(
    response: GetExecutionResponse,
    *,
    include_metadata: bool = True,
    include_appendices: bool | None = None,
    start_index: int = 0,
    full_output: bool = False,
) -> int:
    output = OutputService.get()
    if output.format == OutputFormat.JSON:
        output.raw(response)
        return len(response.activity)

    should_render_appendices = (
        include_metadata if include_appendices is None else include_appendices
    )
    rendered = render_execution_snapshot(
        response,
        include_metadata=include_metadata,
        include_appendices=should_render_appendices,
        start_index=start_index,
        full_output=full_output,
    )
    if rendered:
        output.text(rendered)

    return len(response.activity)


async def watch_execution_live(
    *,
    client: Any,
    execution_id: UUID,
    timeout: float | None,
    full_output: bool,
) -> None:
    output = OutputService.get()
    start_time = time.monotonic() if timeout is not None else None
    response = await client.get_execution(execution_id)

    with Live(
        _to_live_renderable(response, full_output=full_output),
        console=output.console,
        auto_refresh=False,
        refresh_per_second=4,
        vertical_overflow="visible",
        transient=False,
        redirect_stdout=False,
        redirect_stderr=False,
    ) as live:
        live.refresh()
        while not response.is_terminal:
            if _watch_timed_out(start_time=start_time, timeout=timeout):
                output.progress(f"Watch timed out after {timeout:.0f}s.")
                return
            await asyncio.sleep(_LIVE_POLL_INTERVAL_SECONDS)
            response = await client.get_execution(execution_id)
            live.update(
                _to_live_renderable(response, full_output=full_output), refresh=True
            )


def _to_live_renderable(response: GetExecutionResponse, *, full_output: bool) -> Text:
    return Text.from_markup(
        render_execution_snapshot(
            response,
            include_metadata=True,
            include_appendices=response.is_terminal,
            full_output=full_output,
        )
    )


def _watch_timed_out(start_time: float | None, timeout: float | None) -> bool:
    if timeout is None:
        return False
    if start_time is None:
        raise RuntimeError("watch timeout requires a start time")
    return time.monotonic() - start_time >= timeout
