# Falyx CLI Framework — (c) 2025 rtj.dev LLC — MIT Licensed
"""
execution_registry.py

This module provides the `ExecutionRegistry`, a global class for tracking and
introspecting the execution history of Falyx actions.

The registry captures `ExecutionContext` instances from all executed actions, making it
easy to debug, audit, and visualize workflow behavior over time. It supports retrieval,
filtering, clearing, and formatted summary display.

Core Features:
- Stores all action execution contexts globally (with access by name).
- Provides live execution summaries in a rich table format.
- Enables creation of a built-in Falyx Action to print history on demand.
- Integrates with Falyx's introspectable and hook-driven execution model.

Intended for:
- Debugging and diagnostics
- Post-run inspection of CLI workflows
- Interactive tools built with Falyx

Example:
    from falyx.execution_registry import ExecutionRegistry as er
    er.record(context)
    er.summary()
"""
from __future__ import annotations

from collections import defaultdict
from datetime import datetime
from typing import Dict, List

from rich import box
from rich.console import Console
from rich.table import Table

from falyx.context import ExecutionContext
from falyx.logger import logger
from falyx.themes import OneColors


class ExecutionRegistry:
    """
    Global registry for recording and inspecting Falyx action executions.

    This class captures every `ExecutionContext` generated by a Falyx `Action`,
    `ChainedAction`, or `ActionGroup`, maintaining both full history and
    name-indexed access for filtered analysis.

    Methods:
        - record(context): Stores an ExecutionContext, logging a summary line.
        - get_all(): Returns the list of all recorded executions.
        - get_by_name(name): Returns all executions with the given action name.
        - get_latest(): Returns the most recent execution.
        - clear(): Wipes the registry for a fresh run.
        - summary(): Renders a formatted Rich table of all execution results.

    Use Cases:
        - Debugging chained or factory-generated workflows
        - Viewing results and exceptions from multiple runs
        - Embedding a diagnostic command into your CLI for user support

    Note:
        This registry is in-memory and not persistent. It's reset each time the process
        restarts or `clear()` is called.

    Example:
        ExecutionRegistry.record(context)
        ExecutionRegistry.summary()
    """

    _store_by_name: Dict[str, List[ExecutionContext]] = defaultdict(list)
    _store_all: List[ExecutionContext] = []
    _console = Console(color_system="auto")

    @classmethod
    def record(cls, context: ExecutionContext):
        """Record an execution context."""
        logger.debug(context.to_log_line())
        cls._store_by_name[context.name].append(context)
        cls._store_all.append(context)

    @classmethod
    def get_all(cls) -> List[ExecutionContext]:
        return cls._store_all

    @classmethod
    def get_by_name(cls, name: str) -> List[ExecutionContext]:
        return cls._store_by_name.get(name, [])

    @classmethod
    def get_latest(cls) -> ExecutionContext:
        return cls._store_all[-1]

    @classmethod
    def clear(cls):
        cls._store_by_name.clear()
        cls._store_all.clear()

    @classmethod
    def summary(cls):
        table = Table(title="[📊] Execution History", expand=True, box=box.SIMPLE)

        table.add_column("Name", style="bold cyan")
        table.add_column("Start", justify="right", style="dim")
        table.add_column("End", justify="right", style="dim")
        table.add_column("Duration", justify="right")
        table.add_column("Status", style="bold")
        table.add_column("Result / Exception", overflow="fold")

        for ctx in cls.get_all():
            start = (
                datetime.fromtimestamp(ctx.start_time).strftime("%H:%M:%S")
                if ctx.start_time
                else "n/a"
            )
            end = (
                datetime.fromtimestamp(ctx.end_time).strftime("%H:%M:%S")
                if ctx.end_time
                else "n/a"
            )
            duration = f"{ctx.duration:.3f}s" if ctx.duration else "n/a"

            if ctx.exception:
                status = f"[{OneColors.DARK_RED}]❌ Error"
                result = repr(ctx.exception)
            else:
                status = f"[{OneColors.GREEN}]✅ Success"
                result = repr(ctx.result)
                if len(result) > 1000:
                    result = f"{result[:1000]}..."

            table.add_row(ctx.name, start, end, duration, status, result)

        cls._console.print(table)
