#
#   Imandra Inc.
#
#   speclogician/commands/cmd_ch_trace.py
#

from __future__ import annotations

from typing import Optional

import typer

from .utils import get_state
from .runner import run_change_and_render

from ..state.change import (
    AddTestTrace,
    EditTestTrace,
    AddLogTrace,
    EditLogTrace,
    RemoveTraceArtifact
)


app = typer.Typer(
    help=(
        "Trace artifacts (TestTrace, LogTrace).\n\n"
        "Traces are concrete evidence. They do not replace scenarios."
    )
)

@app.command(
    "test-add",
    help="Add a new TestTrace artifact (legacy single-step: given/when/then).",
)
def add_test_trace(
    ctx: typer.Context,
    art_id: str = typer.Option(..., "--art-id", help="Artifact ID (uuid string)."),
    name: str = typer.Option(..., "--name", help="Test name (not required to be unique)."),
    given: str = typer.Option(..., "--given", help="Concrete *state* expression (IML literal), e.g. '{ x = 1 }'."),
    when: Optional[str] = typer.Option(None, "--when", help="Concrete *action* expression (IML literal)."),
    then: Optional[str] = typer.Option(None, "--then", help="Concrete resulting *state* expression (IML literal)."),
    filepath: str = typer.Option("", "--filepath", help="Original test filepath (optional)."),
    language: Optional[str] = typer.Option(None, "--language", help="Source language (optional)."),
    contents: Optional[str] = typer.Option(None, "--contents", help="Original test contents (optional)."),
    json_only: bool = typer.Option(False, "--json", help="Emit JSON only."),
) -> None:
    """
    Examples:

      speclogician ch trace test-add --art-id T1 --name test1 --given "{ x = 1 }" --when "{ y = 2 }" --then "{ x = 3 }"

      speclogician ch trace test-add --art-id T1 --name test1 --given "{ x = 1 }" --json
    """
    state = get_state(ctx, json_mode=json_only)
    change = AddTestTrace(
        art_id=art_id,
        name=name,
        given=given,
        when=when,
        then=then,
        filepath=filepath,
        language=language,
        contents=contents,
    )
    run_change_and_render(state, change, json_only=json_only)
    state.save()


@app.command(
    "test-edit",
    help="Edit an existing TestTrace artifact by art_id.",
)
def edit_test_trace(
    ctx: typer.Context,
    art_id: str = typer.Argument(..., help="Artifact ID to edit."),
    name: Optional[str] = typer.Option(None, "--name", help="Update the trace name."),
    given: Optional[str] = typer.Option(None, "--given", help="Update given (state literal)."),
    when: Optional[str] = typer.Option(None, "--when", help="Update when (action literal)."),
    then: Optional[str] = typer.Option(None, "--then", help="Update then (state literal)."),
    filepath: Optional[str] = typer.Option(None, "--filepath", help="Update filepath."),
    language: Optional[str] = typer.Option(None, "--language", help="Update language."),
    contents: Optional[str] = typer.Option(None, "--contents", help="Update contents."),
    json_only: bool = typer.Option(False, "--json", help="Emit JSON only."),
) -> None:
    """
    Example:

      speclogician ch trace test-edit T1 --then "{ x = 999 }" --json
    """
    state = get_state(ctx, json_mode=json_only)
    change = EditTestTrace(
        art_id=art_id,
        name=name,
        given=given,
        when=when,
        then=then,
        filepath=filepath,
        language=language,
        contents=contents,
    )
    run_change_and_render(state, change, json_only=json_only)
    state.save()


@app.command(
    "log-add",
    help="Add a new LogTrace artifact (legacy single-step: given/when/then + raw log contents).",
)
def add_log_trace(
    ctx: typer.Context,
    art_id: str = typer.Option(..., "--art-id", help="Artifact ID (uuid string)."),
    filename: str = typer.Option(..., "--filename", help="Log filename (not required to be unique)."),
    contents: str = typer.Option(..., "--contents", help="Raw log contents."),
    given: str = typer.Option(..., "--given", help="Concrete state expression (IML literal)."),
    when: Optional[str] = typer.Option(None, "--when", help="Concrete action expression (IML literal)."),
    then: Optional[str] = typer.Option(None, "--then", help="Concrete resulting state expression (IML literal)."),
    json_only: bool = typer.Option(False, "--json", help="Emit JSON only."),
) -> None:
    """
    Example:

      speclogician ch trace log-add --art-id L1 --filename svc.log --contents "..." --given "{ x = 1 }" --when "{ y = 2 }"
    """
    state = get_state(ctx, json_mode=json_only)
    change = AddLogTrace(
        art_id=art_id,
        filename=filename,
        contents=contents,
        given=given,
        when=when,
        then=then,
    )
    run_change_and_render(state, change, json_only=json_only)
    state.save()


@app.command(
    "log-edit",
    help="Edit an existing LogTrace artifact by art_id.",
)
def edit_log_trace(
    ctx: typer.Context,
    art_id: str = typer.Argument(..., help="Artifact ID to edit."),
    filename: Optional[str] = typer.Option(None, "--filename", help="Update filename."),
    contents: Optional[str] = typer.Option(None, "--contents", help="Update raw log contents."),
    given: Optional[str] = typer.Option(None, "--given", help="Update given (state literal)."),
    when: Optional[str] = typer.Option(None, "--when", help="Update when (action literal)."),
    then: Optional[str] = typer.Option(None, "--then", help="Update then (state literal)."),
    json_only: bool = typer.Option(False, "--json", help="Emit JSON only."),
) -> None:
    """
    Example:

      speclogician ch trace log-edit L1 --contents "new log line" --json
    """
    state = get_state(ctx, json_mode=json_only)
    change = EditLogTrace(
        art_id=art_id,
        filename=filename,
        contents=contents,
        given=given,
        when=when,
        then=then,
    )
    run_change_and_render(state, change, json_only=json_only)
    state.save()


@app.command(
    "remove",
    help="Remove a trace artifact (TestTrace or LogTrace) by art_id.",
)
def remove_trace(
    ctx: typer.Context,
    art_id: str = typer.Argument(..., help="Artifact ID to remove."),
    json_only: bool = typer.Option(False, "--json", help="Emit JSON only."),
) -> None:
    """
    Example:

      speclogician ch trace remove T1 --json
    """
    state = get_state(ctx, json_mode=json_only)
    change = RemoveTraceArtifact(art_id=art_id)
    run_change_and_render(state, change, json_only=json_only)
    state.save()
