"""Generate golden HTML/aser test data from the Python evaluator.

Evaluates curated component calls through the Python ref evaluator and
writes golden_data.json — a list of {name, sx_input, expected_html,
expected_aser} triples.

Usage:
    python3 shared/sx/tests/generate_golden.py
"""

import asyncio
import json
import os
import sys

_project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
if _project_root not in sys.path:
    sys.path.insert(0, _project_root)


# Curated test cases — component calls and plain expressions that
# exercise the rendering pipeline.  Each entry is (name, sx_input).
GOLDEN_CASES = [
    # --- Basic HTML rendering ---
    ("div_simple", '(div "hello")'),
    ("div_class", '(div :class "card" "content")'),
    ("p_text", '(p "paragraph text")'),
    ("nested_tags", '(div (p "a") (p "b"))'),
    ("void_br", "(br)"),
    ("void_hr", "(hr)"),
    ("void_img", '(img :src "/photo.jpg" :alt "A photo")'),
    ("void_input", '(input :type "text" :name "q" :placeholder "Search")'),
    ("fragment", '(<> (p "a") (p "b"))'),
    ("boolean_attr", '(input :type "checkbox" :checked true)'),
    ("nil_attr", '(div :class nil "content")'),
    ("empty_string_attr", '(div :class "" "visible")'),

    # --- Control flow ---
    ("if_true", '(if true (p "yes") (p "no"))'),
    ("if_false", '(if false (p "yes") (p "no"))'),
    ("when_true", '(when true (p "shown"))'),
    ("when_false", '(when false (p "hidden"))'),
    ("let_binding", '(let ((x "hi")) (p x))'),
    ("let_multiple", '(let ((x "a") (y "b")) (div (p x) (p y)))'),
    ("cond_form", '(cond (= 1 2) (p "no") (= 1 1) (p "yes") :else (p "default"))'),
    ("case_form", '(case "b" "a" "A" "b" "B" :else "?")'),
    ("and_short", '(and true false)'),
    ("or_short", '(or false "found")'),

    # --- Higher-order forms ---
    ("map_li", '(map (fn (x) (li x)) (list "a" "b" "c"))'),
    ("filter_even", '(filter even? (list 1 2 3 4 5))'),
    ("reduce_sum", '(reduce + 0 (list 1 2 3 4 5))'),

    # --- String operations ---
    ("str_concat", '(str "hello" " " "world")'),
    ("str_upcase", '(upcase "hello")'),

    # --- Component definitions and calls ---
    ("defcomp_simple",
     '(do (defcomp ~test-badge (&key label) (span :class "badge" label)) (~test-badge :label "New"))'),
    ("defcomp_children",
     '(do (defcomp ~test-wrap (&rest children) (div :class "wrap" children)) (~test-wrap (p "inside")))'),
    ("defcomp_multi_key",
     '(do (defcomp ~test-card (&key title subtitle) (div (h2 title) (when subtitle (p subtitle)))) '
     '(~test-card :title "Title" :subtitle "Sub"))'),
    ("defcomp_no_optional",
     '(do (defcomp ~test-card2 (&key title subtitle) (div (h2 title) (when subtitle (p subtitle)))) '
     '(~test-card2 :title "Only Title"))'),

    # --- Nested components ---
    ("nested_components",
     '(do (defcomp ~inner (&key text) (span :class "inner" text)) '
     '(defcomp ~outer (&key title &rest children) (div :class "outer" (h2 title) children)) '
     '(~outer :title "Hello" (~inner :text "World")))'),

    # --- Macros ---
    ("macro_unless",
     '(do (defmacro unless (cond &rest body) (list \'if (list \'not cond) (cons \'do body))) '
     '(unless false (p "shown")))'),

    # --- Special rendering patterns ---
    ("do_block", '(div (do (p "a") (p "b")))'),
    ("nil_child", '(div nil "after-nil")'),
    ("number_child", '(div 42)'),
    ("bool_child", '(div true)'),

    # --- Data attributes ---
    ("data_attr", '(div :data-id "123" :data-name "test" "content")'),

    # --- raw! (inject pre-rendered HTML) ---
    ("raw_simple", '(raw! "<b>bold</b>")'),
    ("raw_in_div", '(div (raw! "<em>italic</em>"))'),
    ("raw_component",
     '(do (defcomp ~rich (&key html) (raw! html)) '
     '(~rich :html "<p>CMS</p>"))'),

    # --- Shared template components (if available) ---
    ("misc_error_inline",
     '(do (defcomp ~shared:misc/error-inline (&key (message :as string)) '
     '(div :class "text-red-600 text-sm" message)) '
     '(~shared:misc/error-inline :message "Something went wrong"))'),
    ("misc_notification_badge",
     '(do (defcomp ~shared:misc/notification-badge (&key (count :as number)) '
     '(span :class "bg-red-500 text-white text-xs rounded-full px-1.5 py-0.5" count)) '
     '(~shared:misc/notification-badge :count 5))'),
    ("misc_cache_cleared",
     '(do (defcomp ~shared:misc/cache-cleared (&key (time-str :as string)) '
     '(span :class "text-green-600 font-bold" "Cache cleared at " time-str)) '
     '(~shared:misc/cache-cleared :time-str "12:00"))'),
    ("misc_error_list_item",
     '(do (defcomp ~shared:misc/error-list-item (&key (message :as string)) (li message)) '
     '(~shared:misc/error-list-item :message "Bad input"))'),
    ("misc_fragment_error",
     '(do (defcomp ~shared:misc/fragment-error (&key (service :as string)) '
     '(p :class "text-sm text-red-600" "Service " (b service) " is unavailable.")) '
     '(~shared:misc/fragment-error :service "blog"))'),
]


def _generate_html(sx_input: str) -> str:
    """Evaluate SX and render to HTML using the Python evaluator."""
    from shared.sx.ref.sx_ref import evaluate, render
    from shared.sx.parser import parse_all

    env = {}
    exprs = parse_all(sx_input)

    # For multi-expression inputs (defcomp then call), use evaluate
    # to process all defs, then render the final expression
    if len(exprs) > 1:
        # Evaluate all expressions — defs install into env
        result = None
        for expr in exprs:
            result = evaluate(expr, env)
        # Render the final result
        return render(result, env)
    else:
        # Single expression — render directly
        return render(exprs[0], env)


def main():
    golden = []
    ok = 0
    failed = 0
    for name, sx_input in GOLDEN_CASES:
        try:
            html = _generate_html(sx_input)
            golden.append({
                "name": name,
                "sx_input": sx_input,
                "expected_html": html,
            })
            ok += 1
        except Exception as e:
            print(f"  SKIP {name}: {e}")
            failed += 1

    outpath = os.path.join(os.path.dirname(__file__), "golden_data.json")
    with open(outpath, "w") as f:
        json.dump(golden, f, indent=2, ensure_ascii=False)

    print(f"Generated {ok} golden cases ({failed} skipped) → {outpath}")


if __name__ == "__main__":
    main()
