"""Command-line interface for dynojson."""

from __future__ import annotations

import argparse
import json
import os
import sys

from . import __version__, get_property, marshall, unmarshall


def _read_input(source: str) -> str:
    """Return raw JSON from a string literal, file path, or stdin (``-``)."""
    if source == "-":
        return sys.stdin.read()

    # Inline JSON
    stripped = source.strip()
    if stripped.startswith("{") or stripped.startswith("["):
        return stripped

    # File path
    path = os.path.abspath(source)
    if not os.path.exists(path):
        raise FileNotFoundError(f"File not found: {path}")
    with open(path, encoding="utf-8") as fh:
        return fh.read()


def _trim_json(raw: str) -> str:
    """Strip leading/trailing non-JSON characters (e.g. shell quotes)."""
    raw = raw.strip().strip("'\"")
    start = min(
        (raw.find("{") if "{" in raw else len(raw)),
        (raw.find("[") if "[" in raw else len(raw)),
    )
    end = max(
        (raw.rfind("}") if "}" in raw else -1),
        (raw.rfind("]") if "]" in raw else -1),
    )
    if start <= end:
        return raw[start : end + 1]
    return raw


def _build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="dynojson",
        description="Marshall/unmarshall JSON to/from DynamoDB JSON format",
    )
    parser.add_argument(
        "--version", action="version", version=f"%(prog)s {__version__}"
    )

    sub = parser.add_subparsers(dest="command", metavar="<command>")

    for cmd, aliases, help_text in [
        ("unmarshall", ["u"], "Convert DynamoDB JSON to regular JSON"),
        ("marshall", ["m"], "Convert regular JSON to DynamoDB JSON"),
    ]:
        p = sub.add_parser(cmd, aliases=aliases, help=help_text)
        p.add_argument(
            "json",
            metavar="JSON",
            help="JSON string, file path, or '-' to read from stdin",
        )
        p.add_argument(
            "-g",
            "--get",
            metavar="PATH",
            default=None,
            help="Extract a property before converting (dot-separated, supports *)",
        )

    return parser


def main(argv: list[str] | None = None) -> None:  # noqa: C901
    parser = _build_parser()
    args = parser.parse_args(argv)

    if args.command is None:
        parser.print_help()
        sys.exit(0)

    action = "unmarshall" if args.command in ("unmarshall", "u") else "marshall"

    try:
        raw = _read_input(args.json)
        json_str = _trim_json(raw)

        if args.get:
            json_str = get_property(json_str, args.get)

        if action == "marshall":
            result = marshall(json_str)
        else:
            result = unmarshall(json_str)

        # Pretty-print so output is readable
        parsed = json.loads(result)
        print(json.dumps(parsed))
        sys.exit(0)

    except (FileNotFoundError, ValueError) as exc:
        print(f"Error: {exc}", file=sys.stderr)
        sys.exit(1)
    except KeyboardInterrupt:
        sys.exit(130)


if __name__ == "__main__":
    main()
