"""Extract CLI spec from Typer apps as JSON.

Introspects the eval and doc Typer apps and dumps a JSON spec
that the shell wrapper can use for command validation.

Usage:
    uv run python src/cl-lite/extract_spec.py > src/cl-lite/shell/spec.json
"""

import json
import sys

import click
import typer.main as tm

from codelogician.commands.cmd_doc import app as doc_app
from codelogician.commands.cmd_eval import app as eval_app

# Argument names that indicate the command reads IML input.
# When the shell wrapper sees one of these, it knows to handle .iml file / stdin.
IML_ARG_NAMES = {'file', 'iml_path'}


def extract_command(cmd: click.Command) -> dict:
    """Extract spec from a single Click command."""
    args = []
    options = []
    accepts_iml = False
    for param in cmd.params:
        info = {
            'name': param.name,
            'required': param.required,
            'type': param.type.name.upper()
            if hasattr(param.type, 'name')
            else 'STRING',
        }
        if isinstance(param, click.Argument):
            if param.name in IML_ARG_NAMES:
                accepts_iml = True
            args.append(info)
        else:
            assert isinstance(param, click.Option)
            option_name = param.opts[0] if param.opts else f'--{param.name}'
            info['flag'] = option_name
            options.append(info)
    result: dict = {'accepts_iml': accepts_iml, 'args': args, 'options': options}
    if cmd.help:
        result['help'] = cmd.help
    return result


def extract_group(group: click.Command) -> dict:
    """Extract spec from a Click group (Typer app)."""
    assert isinstance(group, click.Group)
    commands = {}
    for name, cmd in group.commands.items():
        if isinstance(cmd, click.Group):
            commands[name] = {
                'type': 'group',
                'commands': {
                    sname: extract_command(scmd) for sname, scmd in cmd.commands.items()
                },
            }
        else:
            commands[name] = extract_command(cmd)
    return commands


def main():
    eval_click: click.Command = tm.get_command(eval_app)
    doc_click: click.Command = tm.get_command(doc_app)

    spec = {
        'eval': extract_group(eval_click),
        'doc': extract_group(doc_click),
    }

    json.dump(spec, sys.stdout, indent=2)
    sys.stdout.write('\n')


if __name__ == '__main__':
    main()
