#
#   Imandra Inc.
#
#   speclogician/main.py
#

__version__ = "1.0"

import typer
from typing import Final
from pathlib import Path

from speclogician.commands import ch_app, view_app, find_app
from speclogician.presentation.preview import preview_app
from speclogician.demos.cmd_demo import app as demo_app
from speclogician.utils import console, require_imandra_key
from speclogician.utils.load import load_state
from speclogician.state.state import State


SL_HELP : Final = """
:robot: [bold deep_sky_blue1][italic]SpecLogician[/italic] is an AI framework for data-driven 
formal program specification synthesis, verification, and analysis.[/bold deep_sky_blue1] :rocket:

SpecLogician provides an LLM-native alternative to classical predicate abstraction and
Counterexample-Guided Abstraction Refinement (CEGAR).

Instead of requiring users or agents to discover and maintain global Boolean abstractions,
SpecLogician models behavior directly using structured scenarios composed from state and
action predicates. Specifications are built incrementally by defining a domain base
(types and core invariants), predicates, transition functions, and scenarios that capture
intent explicitly.

Each change creates a new immutable state, triggers precise logical analysis, and produces
a semantic diff against the previous state. Using ImandraX’s exact symbolic reasoning and
region decomposition, SpecLogician computes coverage, conflicts, reachability, and
behavioral complements—identifying real, reachable behaviors not yet explained by any
scenario.

Rather than reacting to spurious counterexamples, users and AI agents receive positive,
actionable refinement signals in the form of uncovered or inconsistent behavioral regions.
This enables scalable, explainable refinement toward correctness and completeness across
source code, tests, documentation, and real-world traces—using small, explicit change
commands instead of manual file editing.

Learn more at [bold italic deep_sky_blue1]https://www.speclogician.dev![/bold italic deep_sky_blue1]
"""

app = typer.Typer(
    name="SpecLogician",
    help=SL_HELP,
    rich_markup_mode="rich",
    no_args_is_help=True
)

class AppCtx:
    state_json_path: Path | None = None
    state: State | None = None

@app.callback()
def _cli_entry() -> None:
    """
    SpecLogician CLI entry point.
    """
    require_imandra_key()

@app.callback()
def main(ctx: typer.Context, state_json: Path | None = typer.Option(None, "--state-json", help="...")):
    ctx.obj = ctx.ensure_object(AppCtx)

    ctx.obj.state_json_path = state_json

    if state_json:
        try:
            ctx.obj.state = load_state(path=state_json)
        except Exception:
            ctx.obj.state = None
    else:
        ctx.obj.state = None  # ✅ ensure attribute exists

app.add_typer(view_app      , name="view"       , help="View components of the state")
app.add_typer(ch_app        , name="ch"         , help="Apply change to the state")
app.add_typer(find_app      , name="find"       , help="Search for a components of the state")
app.add_typer(demo_app      , name="demo"       , help="A collection of SpecLogician demos")
app.add_typer(preview_app   , name="preview"    , hidden=True) # This is an internal tool for manually inspecting renderers


@app.command(help="Run the TUI")
def tui ():
    """ Run the TUI to help us navigate and explore the state """
    from speclogician.tui.app import SpecLogicianApp
    tui_app = SpecLogicianApp()
    tui_app.run()

@app.command(help="Generate a prompt for helping agents use SpecLogician")
def prompt(
    out: Path | None = typer.Option(
        None,
        "--out",
        help="Write the prompt to a file instead of stdout.",
    ),
):
    """
    Generate a prompt for helping CLI agents use SpecLogician.
    """

    here = Path(__file__).resolve()
    repo_root = here.parents[0]
    prompt_path = repo_root / "utils" / "prompt.md"

    try:
        txt = prompt_path.read_text()
    except Exception as e:
        console.print(f"🛑 Failed to read prompt file at {prompt_path}: {e}")
        raise typer.Exit(2)

    if out:
        try:
            out.write_text(txt)
        except Exception as e:
            console.print(f"🛑 Failed to write prompt to {out}: {e}")
            raise typer.Exit(2)
        typer.echo(str(out))
    else:
        typer.echo(txt)
    

@app.command()
def version(
    json_only: bool = typer.Option(False, "--json", help="Output JSON"),
):
    """Print SpecLogician version"""
    if json_only:
        typer.echo(f'{{"version": "{__version__}"}}')
    else:
        typer.echo(__version__)

if __name__ == '__main__':
    app()
