#!/usr/bin/env python3

import argparse
import importlib
import importlib.util
import json
import os
import sys

import jsee
from jsee.jsee import DEFAULT_MAX_BODY_BYTES

if len(sys.argv) > 1 and sys.argv[1] in ('inspect', 'validate', 'run', 'serve', 'artifact-import'):
  from jsee.transport import command
  output = sys.stdout
  if sys.argv[1] != 'serve':
    # Keep prints from model imports/functions off the machine-readable output channel.
    sys.stdout = sys.stderr
  try:
    result, exit_code = command(sys.argv[1:])
  except (RuntimeError, ValueError, OSError) as error:
    print(str(error), file=sys.stderr)
    sys.exit(1)
  if result is not None:
    output.write(json.dumps(result, allow_nan=False) + '\n')
  sys.exit(exit_code)


# ── jsee init <template> ────────────────────────────────────────────
if len(sys.argv) >= 2 and sys.argv[1] == 'init':
  tpl = sys.argv[2] if len(sys.argv) >= 3 else 'minimal'
  cwd = os.getcwd()

  if tpl == 'chat':
    files = {
      'schema.json': json.dumps({
        'model': {'url': 'model.py', 'name': 'chat', 'type': 'post'},
        'inputs': [{'name': 'message', 'type': 'string', 'enter': True}],
        'outputs': [{'name': 'chat', 'type': 'chat'}]
      }, indent=2) + '\n',
      'model.py': '''def chat(message='', history=None):
    return {'chat': 'You said: ' + message}
''',
      'README.md': '''# Chat

A chat app built with [JSEE](https://jsee.org).

## Run

```bash
jsee schema.json
```
'''
    }
  else:
    # minimal (default)
    files = {
      'schema.json': json.dumps({
        'model': {'url': 'model.py', 'name': 'greet', 'type': 'post'},
        'inputs': [
          {'name': 'name', 'type': 'string', 'default': 'World'},
          {'name': 'count', 'type': 'slider', 'min': 1, 'max': 10, 'default': 3}
        ],
        'outputs': [{'name': 'greeting', 'type': 'code'}]
      }, indent=2) + '\n',
      'model.py': '''def greet(name='World', count=3):
    return {'greeting': (name + '\\n') * count}
''',
      'README.md': '''# My App

A web app built with [JSEE](https://jsee.org).

## Run

```bash
jsee schema.json
```
'''
    }

  created = 0
  for filename, content in files.items():
    dest = os.path.join(cwd, filename)
    if os.path.exists(dest):
      print(f'{filename} already exists, skipping')
    else:
      with open(dest, 'w') as f:
        f.write(content)
      print(f'Created {filename}')
      created += 1

  if created == 0:
    print('All files already exist, nothing to do')

  sys.exit(0)


def detect_arg_value(value):
  """Auto-detect value type: number, JSON, file path, or string."""
  if not isinstance(value, str):
    return value
  # Number
  try:
    if '.' in value:
      return float(value)
    return int(value)
  except ValueError:
    pass
  # JSON array or object
  if value.startswith('[') or value.startswith('{'):
    try:
      return json.loads(value)
    except (json.JSONDecodeError, ValueError):
      pass
  # File path
  if os.path.isfile(value):
    return value
  return value


parser = argparse.ArgumentParser(
  description='JSEE — turn Python functions into web apps with GUI and REST API',
  epilog='''data inputs:
  Extra positional args after function name are mapped to function params in order.
  Extra --key=value args are matched by parameter name.
  Values are auto-detected: numbers, JSON arrays/objects, file paths, or strings.
  Inputs set from the CLI are locked (non-editable) in the GUI.

examples:
  jsee example.py greet                    Serve function with GUI
  jsee example.py greet Alice 5            Pass positional data
  jsee example.py greet --name=Alice       Pass named data
  jsee schema.json                         Serve from schema file''',
  formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument('target', help='Python file with function (e.g. example.py) or schema.json')
parser.add_argument('function', nargs='?', default=None, help='Function name (required for .py files)')
parser.add_argument('--host', default='127.0.0.1', help='Host to bind to (default: 127.0.0.1)')
parser.add_argument('--port', type=int, default=5050, help='Port to listen on (default: 5050)')
parser.add_argument('--max-body-bytes', type=int, default=DEFAULT_MAX_BODY_BYTES,
                    help='Maximum request body size in bytes (default: 52428800)')

from jsee.host_policy import add_cli_options
add_cli_options(parser)
args, extra = parser.parse_known_args()

# Parse extra --key=value args into defaults dict
defaults = {}
extra_positional = []
for arg in extra:
  if arg.startswith('--') and '=' in arg:
    key, val = arg[2:].split('=', 1)
    defaults[key] = detect_arg_value(val)
  elif arg.startswith('--'):
    # --key without value treated as boolean True
    defaults[arg[2:]] = True
  else:
    extra_positional.append(detect_arg_value(arg))

sys.path.insert(1, os.getcwd())

if args.target.endswith('.json'):
  if args.function is not None:
    extra_positional.insert(0, detect_arg_value(args.function))
  # Keep the descriptor path so model and static URLs retain their app root.
  jsee.serve(args.target, args.host, args.port, defaults=defaults,
             extra_positional=extra_positional, max_body_bytes=args.max_body_bytes, auth_token=args.auth_token,
             auth_token_env=args.auth_token_env, cors_origins=args.cors_origins)
elif args.target.endswith('.py'):
  # Function mode
  if not args.function:
    print('Error: function name required for .py files', file=sys.stderr)
    print('Usage: jsee example.py function_name', file=sys.stderr)
    sys.exit(1)
  module_name = os.path.splitext(os.path.basename(args.target))[0]
  spec = importlib.util.spec_from_file_location(module_name, os.path.abspath(args.target))
  module = importlib.util.module_from_spec(spec)
  spec.loader.exec_module(module)
  target = getattr(module, args.function)
  jsee.serve(target, args.host, args.port, defaults=defaults,
             extra_positional=extra_positional, max_body_bytes=args.max_body_bytes, auth_token=args.auth_token,
             auth_token_env=args.auth_token_env, cors_origins=args.cors_origins)
else:
  # Try as module name (legacy compat)
  module = importlib.import_module(args.target.split('.')[0])
  if args.function:
    target = getattr(module, args.function)
    jsee.serve(target, args.host, args.port, defaults=defaults,
               extra_positional=extra_positional, max_body_bytes=args.max_body_bytes, auth_token=args.auth_token,
               auth_token_env=args.auth_token_env, cors_origins=args.cors_origins)
  else:
    print('Error: function name required', file=sys.stderr)
    sys.exit(1)
