"""
Runs CLI commands for managing module executions.
"""

import json
import time

import click

from ..client import APIClient, APIError
from .utils import console, error_console, verbose_option


def _normalize_run_status(payload) -> str:
    if not isinstance(payload, dict):
        return ""
    status = payload.get("status") or payload.get("state")
    return str(status).strip().lower() if status is not None else ""


def _render_log_events(payload) -> bool:
    if not isinstance(payload, dict):
        return False

    events = payload.get("events", [])
    if events:
        for event in events:
            console.print(event.get("message", "").strip())
        return True

    message = str(payload.get("message") or "").strip()
    logs_url = str(payload.get("logsUrl") or "").strip()
    if message:
        console.print(message)
    if logs_url:
        console.print(logs_url)
    return False


@click.group()
def runs():
    """Manage and monitor module remote executions"""
    pass


@runs.command(name="status")
@click.argument("run_id")
@verbose_option
def status_cmd(run_id):
    """Check the status of a specific run"""
    client = APIClient()
    try:
        run = client.runs.get(run_id)
        if hasattr(run, "model_dump"):
            run = run.model_dump()
        console.print(json.dumps(run, indent=2))
    except APIError as e:
        error_console.print(f"[red]Error:[/red] {e}")
        raise click.Abort() from e


@runs.command()
@click.argument("run_id")
@click.option("--follow", "-f", is_flag=True, help="Follow log output continuously")
@verbose_option
def logs(run_id, follow):
    """Fetch CloudWatch logs for a run"""
    client = APIClient()
    try:
        if not follow:
            res = client.runs.logs(run_id)
            val = res if isinstance(res, dict) else (res.model_dump() if hasattr(res, "model_dump") else {})
            _render_log_events(val)
        else:
            next_token = None
            rendered_empty_notice = False
            while True:
                res = client.runs.logs(run_id, next_token=next_token)
                val = res if isinstance(res, dict) else (res.model_dump() if hasattr(res, "model_dump") else {})
                events = val.get("events", [])
                if events:
                    _render_log_events(val)
                    rendered_empty_notice = False
                elif not rendered_empty_notice:
                    _render_log_events(val)
                    rendered_empty_notice = True
                next_token = val.get("nextForwardToken") or val.get("nextToken")
                if not events:
                    # check if run is done
                    run_info = client.runs.get(run_id)
                    run_val = (
                        run_info
                        if isinstance(run_info, dict)
                        else (run_info.model_dump() if hasattr(run_info, "model_dump") else {})
                    )
                    run_status = _normalize_run_status(run_val)
                    if run_status in {"completed", "failed", "cancelled", "timeout", "timed_out"}:
                        break
                    time.sleep(2)
    except APIError as e:
        error_console.print(f"[red]Error:[/red] {e}")
        raise click.Abort() from e


@runs.command()
@click.argument("run_id")
@verbose_option
def results(run_id):
    """Fetch output results payload for a run"""
    client = APIClient()
    try:
        res = client.runs.results(run_id)
        val = res if isinstance(res, dict) else (res.model_dump() if hasattr(res, "model_dump") else {})
        console.print(json.dumps(val, indent=2))
    except APIError as e:
        error_console.print(f"[red]Error:[/red] {e}")
        raise click.Abort() from e


@runs.command()
@click.argument("run_id")
@verbose_option
def cancel(run_id):
    """Cancel an in-progress run"""
    client = APIClient()
    try:
        client.runs.cancel(run_id)
        console.print(f"[green]Successfully requested cancellation for run {run_id}[/green]")
    except APIError as e:
        error_console.print(f"[red]Error:[/red] {e}")
        raise click.Abort() from e
