"""HTTPie-style key-value parser for CLI options.

Provides parsing for key=value and key:=json syntax.

Syntax:
    key=value   -> {"key": "value"}       (string value)
    key:=value  -> {"key": <parsed JSON>} (raw JSON: numbers, bools, arrays, objects)

Examples:
    name=John              -> {"name": "John"}
    count:=5               -> {"count": 5}
    active:=true           -> {"active": true}
    tags:='["a","b"]'      -> {"tags": ["a", "b"]}
    url=https://foo.com    -> {"url": "https://foo.com"}  (= in value is preserved)

Usage with Typer:
    from cli.infrastructure.kv_parser import kv_pairs_callback

    @app.command()
    def my_command(
        variables: Annotated[
            list[str] | None,
            typer.Option(
                "--var", "-v",
                help="Variables in key=value or key:=json format",
                callback=kv_pairs_callback,
            ),
        ] = None,
    ) -> None:
        # variables is now a dict[str, Any]
        ...
"""

import json
from typing import Any

import typer


def parse_kv_pairs(
    values: list[str] | None,
    *,
    parse_object_array_strings: bool = False,
) -> dict[str, Any]:
    """Parse a list of key=value or key:=json strings into a dictionary.

    Args:
        values: List of strings in "key=value" or "key:=json" format.
                Can be None or empty.
        parse_object_array_strings: When True, valid JSON object/array values passed
                through key=value are parsed into dict/list values.

    Returns:
        Dictionary with parsed key-value pairs. Empty dict if input is None/empty.

    Raises:
        typer.BadParameter: If a value has invalid format or invalid JSON for :=

    Examples:
        >>> parse_kv_pairs(["name=John", "age:=30"])
        {"name": "John", "age": 30}

        >>> parse_kv_pairs(["active:=true", "tags:=['a','b']"])
        {"active": True, "tags": ["a", "b"]}

        >>> parse_kv_pairs(None)
        {}
    """
    if not values:
        return {}

    result: dict[str, Any] = {}

    for item in values:
        # Check for raw JSON syntax first (`:=`)
        if ":=" in item:
            key, raw_value = item.split(":=", 1)
            key = key.strip()

            if not key:
                raise typer.BadParameter(
                    f"Missing key in '{item}'. Expected format: key:=json_value"
                )

            try:
                result[key] = json.loads(raw_value)
            except json.JSONDecodeError as e:
                raise typer.BadParameter(
                    f"Invalid JSON for key '{key}': {raw_value}. Error: {e}"
                )

        # Check for string syntax (`=`)
        elif "=" in item:
            key, value = item.split("=", 1)
            key = key.strip()

            if not key:
                raise typer.BadParameter(
                    f"Missing key in '{item}'. Expected format: key=value"
                )

            stripped_value = value.lstrip()
            if parse_object_array_strings and stripped_value.startswith(("{", "[")):
                try:
                    parsed_value = json.loads(value)
                except json.JSONDecodeError:
                    result[key] = value
                else:
                    result[key] = parsed_value
            else:
                result[key] = value

        else:
            raise typer.BadParameter(
                f"Invalid format: '{item}'. Expected 'key=value' or 'key:=json_value'"
            )

    return result


def kv_pairs_callback(values: list[str] | None) -> dict[str, Any]:
    """Typer callback that transforms key-value pairs into a dictionary.

    Use this as a callback for typer.Option to accept httpie-style key-value
    pairs that get parsed into a dictionary before reaching your command function.

    Args:
        values: List of strings from repeated option usage (e.g., -v a=1 -v b=2)

    Returns:
        Parsed dictionary

    Example:
        @app.command()
        def cmd(
            vars: Annotated[
                list[str] | None,
                typer.Option("--var", "-v", callback=kv_pairs_callback),
            ] = None,
        ):
            # vars is a dict here, not list[str]
            print(vars)  # {"a": "1", "b": 2}
    """
    return parse_kv_pairs(values)
