"""
json_any_endproduct - Python implementations of converters (dependency-free)
"""

import json


def ensure_list(obj):
    return obj if isinstance(obj, list) else [obj]


def to_yaml(obj, indent=2):
    def ser(v, lvl):
        pad = " " * (lvl * indent)
        if v is None:
            return "null"
        if isinstance(v, (int, float, bool)):
            return str(v)
        if isinstance(v, str):
            return v
        if isinstance(v, list):
            if len(v) == 0:
                return "[]"
            return "\\n".join(
                pad
                + "- "
                + (ser(i, lvl + 1) if isinstance(i, (dict, list)) else str(i))
                for i in v
            )
        if isinstance(v, dict):
            if not v:
                return "{}"
            lines = []
            for k, val in v.items():
                if isinstance(val, (dict, list)):
                    lines.append(pad + f"{k}:")
                    lines.append(ser(val, lvl + 1))
                else:
                    lines.append(pad + f"{k}: {val}")
            return "\\n".join(lines)
        return str(v)

    return ser(obj, 0)


def to_csv(obj):
    arr = ensure_list(obj)
    if not arr:
        return ""
    keys = []
    for o in arr:
        for k in o.keys():
            if k not in keys:
                keys.append(k)

    def esc(v):
        if v is None:
            return ""
        s = str(v)
        if "," in s or '"' in s or "\n" in s:
            return '"' + s.replace('"', '""') + '"'
        return s

    rows = [",".join(keys)]
    for o in arr:
        rows.append(",".join(esc(o.get(k)) for k in keys))
    return "\\n".join(rows)


def to_typescript(obj, name="RootObject"):
    example = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = f"export interface {name} {{\\n"
    for k, v in example.items():
        t = "any"
        if isinstance(v, str):
            t = "string"
        elif isinstance(v, bool):
            t = "boolean"
        elif isinstance(v, int):
            t = "number"
        out += f"  {k}: {t};\\n"
    out += "}"
    return out


def to_js(obj):
    return json.dumps(obj, indent=2)


def to_schema_summary(obj):
    example = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )

    def t(v):
        if isinstance(v, list):
            return t(v[0]) + "[]"
        if v is None:
            return "any"
        return type(v).__name__

    return "\\n".join(f"{k}: {t(v)}" for k, v in example.items())


def to_html_table(obj):
    arr = ensure_list(obj)
    keys = []
    for o in arr:
        for k in o.keys():
            if k not in keys:
                keys.append(k)
    out = (
        "<table><thead><tr>"
        + "".join(f"<th>{k}</th>" for k in keys)
        + "</tr></thead><tbody>"
    )
    for o in arr:
        out += "<tr>" + "".join(f"<td>{o.get(k,'')}</td>" for k in keys) + "</tr>"
    out += "</tbody></table>"
    return out


def to_query(obj):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    parts = []
    for k, v in ex.items():
        parts.append(f"{k}={v}")
    return "?" + "&".join(parts) if parts else ""


def to_form_urlencoded(obj):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    return "&".join(f"{k}={v}" for k, v in ex.items())


def to_properties(obj):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    return "\\n".join(f"{k}={v}" for k, v in ex.items())


def to_markdown_table(obj):
    arr = ensure_list(obj)
    keys = []
    for o in arr:
        for k in o.keys():
            if k not in keys:
                keys.append(k)
    head = "| " + " | ".join(keys) + " |"
    sep = "| " + " | ".join(["---"] * len(keys)) + " |"
    rows = "\\n".join(
        "| " + " | ".join(str(o.get(k, "")) for k in keys) + " |" for o in arr
    )
    return "\\n".join([head, sep, rows])


def to_plantuml(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = f"class {name} {{\\n"
    for k in ex.keys():
        out += f"  {k}\\n"
    out += "}"
    return out


def to_mermaid(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = "classDiagram\\n"
    out += f"class {name} {{\\n"
    for k in ex.keys():
        out += f"  {k}\\n"
    out += "}"
    return out


def to_sql_insert(obj, table="mytable"):
    arr = ensure_list(obj)
    stmts = []
    for o in arr:
        cols = ",".join(o.keys())
        vals = ",".join(
            (
                "'" + str(v).replace("'", "''") + "'"
                if isinstance(v, str)
                else ("NULL" if v is None else str(v))
            )
            for v in o.values()
        )
        stmts.append(f"INSERT INTO {table} ({cols}) VALUES ({vals});")
    return "\\n".join(stmts)


def to_bash_export(obj):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    return "\\n".join(f"export {k}='{v}'" for k, v in ex.items())


def to_csharp(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = f"public class {name} {{\\n"
    for k, v in ex.items():
        t = "int" if isinstance(v, int) else "bool" if isinstance(v, bool) else "string"
        out += f"  public {t} {k} {{ get; set; }}\\n"
    out += "}"
    return out


def to_java(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = f"public class {name} {{\\n"
    for k, v in ex.items():
        t = (
            "int"
            if isinstance(v, int)
            else "boolean" if isinstance(v, bool) else "String"
        )
        out += f"  public {t} {k};\\n"
    out += "}"
    return out


def to_python_dataclass(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = "from dataclasses import dataclass\\n\\n"
    out += f"@dataclass\\nclass {name}:\\n"
    for k, v in ex.items():
        t = "int" if isinstance(v, int) else "bool" if isinstance(v, bool) else "str"
        out += f"  {k}: {t}\\n"
    return out


def to_go_struct(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = f"type {name} struct {{\\n"
    for k, v in ex.items():
        t = "int" if isinstance(v, int) else "bool" if isinstance(v, bool) else "string"
        out += f'  {k} {t} `json:"{k}"`\\n'
    out += "}"
    return out


def to_rust_struct(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = f"struct {name} {{\\n"
    for k, v in ex.items():
        t = "i32" if isinstance(v, int) else "bool" if isinstance(v, bool) else "String"
        out += f"  {k}: {t},\\n"
    out += "}"
    return out


def to_dart_class(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = f"class {name} {{\\n"
    for k, v in ex.items():
        t = "int" if isinstance(v, int) else "bool" if isinstance(v, bool) else "String"
        out += f"  final {t} {k};\\n"
    out += f"  {name}({{{', '.join('required '+k for k in ex.keys())}}});\\n"
    out += "}"
    return out


def to_php_array(obj):
    return json.dumps(obj, indent=2)


def to_proto(obj, name="Root"):
    ex = (
        obj[0]
        if isinstance(obj, list) and obj
        else (obj if isinstance(obj, dict) else {})
    )
    out = 'syntax = "proto3";\\nmessage %s {\\n' % name
    i = 1
    for k, v in ex.items():
        t = (
            "int32"
            if isinstance(v, int)
            else "bool" if isinstance(v, bool) else "string"
        )
        out += f"  {t} {k} = {i};\\n"
        i += 1
    out += "}\\n"
    return out


# CLI utility
def cli():
    import sys

    txt = sys.stdin.read()
    data = json.loads(txt)
    fn = sys.argv[1] if len(sys.argv) > 1 else "to_csv"
    mapper = {
        "to_yaml": to_yaml,
        "to_csv": to_csv,
        "to_typescript": to_typescript,
        "to_js": to_js,
        "to_schema_summary": to_schema_summary,
        "to_html_table": to_html_table,
        "to_query": to_query,
        "to_form_urlencoded": to_form_urlencoded,
        "to_properties": to_properties,
        "to_markdown_table": to_markdown_table,
        "to_plantuml": to_plantuml,
        "to_mermaid": to_mermaid,
        "to_sql_insert": to_sql_insert,
        "to_bash_export": to_bash_export,
        "to_csharp": to_csharp,
        "to_java": to_java,
        "to_python_dataclass": to_python_dataclass,
        "to_go_struct": to_go_struct,
        "to_rust_struct": to_rust_struct,
        "to_dart_class": to_dart_class,
        "to_php_array": to_php_array,
        "to_proto": to_proto,
    }
    if fn not in mapper:
        print("Unknown function", fn)
        sys.exit(1)
    print(mapper[fn](data))
