import json
import re
import os

def flatten_for_toon(json_data, parent_key=''):
    """
    Recursively processes JSON data to an intermediate structure suitable for TOON conversion.
    """
    flat_data = {}

    if isinstance(json_data, dict):
        for k, v in json_data.items():
            new_key = f"{parent_key}_{k}" if parent_key else k
            if isinstance(v, list) and v and all(isinstance(item, dict) for item in v):
                headers = list(v[0].keys())
                rows = [[item.get(h) for h in headers] for item in v]
                flat_data[new_key] = {
                    "type": "toon_list",
                    "count": len(v),
                    "headers": headers,
                    "rows": rows
                }
            elif isinstance(v, dict):
                flat_data.update(flatten_for_toon(v, new_key))
            elif isinstance(v, list):
                for i, item in enumerate(v):
                    flat_data[f"{new_key}_{i}"] = item
            else:
                flat_data[new_key] = v
    elif isinstance(json_data, list):
        for i, item in enumerate(json_data):
             flat_data.update(flatten_for_toon(item, f"{parent_key}_{i}"))
    else:
        flat_data[parent_key] = json_data

    return flat_data

def convert_flat_to_toon(flat_data):
    """
    Converts the intermediate flat JSON structure to the TOON string format.
    """
    toon_lines = []
    for key, value in flat_data.items():
        if isinstance(value, dict) and value.get("type") == "toon_list":
            headers_str = ",".join(value["headers"])
            toon_lines.append(f"{key}[{value['count']}]{{{headers_str}}}:")
            for row in value["rows"]:
                row_str = ",".join(map(repr, row))
                toon_lines.append(row_str)
        else:
            toon_lines.append(f"{key}: {value}")
    return "\n".join(toon_lines)

def convert_json_to_toon(input_string):
    """
    Takes a JSON string or a file path, flattens it, and converts it to the TOON string format.
    """
    json_data_str = ""
    # Check if input is a file path that exists
    if os.path.exists(input_string):
        with open(input_string, 'r', encoding='utf-8') as f:
            json_data_str = f.read()
    else:
        # Assume it's a JSON string
        json_data_str = input_string

    try:
        json_data = json.loads(json_data_str)
    except json.JSONDecodeError as e:
        # Raise a more informative error if the string is neither a valid file nor valid JSON
        raise ValueError("Input is not a valid file path or JSON string.") from e

    intermediate_flat_data = flatten_for_toon(json_data)
    toon_output = convert_flat_to_toon(intermediate_flat_data)
    return toon_output

def count_words(text):
    """Counts words by splitting on whitespace."""
    return len(re.findall(r'\b\w+\b', text))

def get_token_proxy_metrics(input_json_string):
    """
    Converts JSON to TOON and calculates proxy metrics (character and word counts).
    """
    input_char_count = len(input_json_string)
    input_word_count = count_words(input_json_string)

    toon_output = convert_json_to_toon(input_json_string)

    output_char_count = len(toon_output)
    output_word_count = count_words(toon_output)

    if input_char_count > 0:
        char_reduction_percent = ((input_char_count - output_char_count) / input_char_count) * 100
    else:
        char_reduction_percent = 0.0

    metrics = {
        "input_json": input_json_string,
        "output_toon": toon_output,
        "metrics": {
            "input": {
                "char_count": input_char_count,
                "word_count": input_word_count
            },
            "output": {
                "char_count": output_char_count,
                "word_count": output_word_count
            },
            "reduction": {
                "char_percent": char_reduction_percent
            }
        }
    }
    return metrics