#
#   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, reach_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 is an LLM-native framework for building and refining precise behavioral specifications 
through explicit, structured modeling rather than implicit abstraction.

Instead of relying on global Boolean summaries or opaque intermediate representations, SpecLogician 
models system behavior directly using scenarios composed from state predicates, action predicates, 
and transitions. Specifications are constructed incrementally by defining a domain base (types and 
core invariants), predicates, transition functions, and scenarios that explicitly capture intended 
behavior.

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

Rather than surfacing abstract errors or opaque failures, SpecLogician provides positive, actionable 
refinement signals: uncovered regions, inconsistent behaviors, unreachable scenarios, and missing intent. 
These signals guide both users and AI agents toward targeted improvements, enabling scalable, explainable 
refinement across source code, tests, documentation, and real-world traces through small, explicit change commands.

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(reach_app     , name="reach"      , help="Scenario reachability analysis (ImandraX-backed compute + graph queries).")
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()
