#!/usr/bin/env bash
set -e

DTOB="$(dirname "$0")/../bin/dtob"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

PASS=0
FAIL=0

# Strip ANSI escape codes and normalize spaces
strip_ansi() {
    sed $'s/\033\\[[0-9;]*m//g' | tr -s ' '
}

run_test() {
    local name="$1" json="$2" expect="$3"
    local infile="$TMPDIR/in.json"
    local outfile="$TMPDIR/out.dtob"

    printf '%s' "$json" > "$infile"
    "$DTOB" encode "$infile" "$outfile" --types 2>/dev/null

    local got
    got=$("$DTOB" represent "$outfile" --flat 2>/dev/null | strip_ansi)

    if [ "$got" = "$expect" ]; then
        echo "  PASS: $name"
        PASS=$((PASS + 1))
    else
        echo "  FAIL: $name"
        echo "    expected: $expect"
        echo "    got:      $got"
        FAIL=$((FAIL + 1))
    fi
}

# run_test_grep: encode, decode, check output contains a pattern
run_test_grep() {
    local name="$1" json="$2" pattern="$3"
    local infile="$TMPDIR/in.json"
    local outfile="$TMPDIR/out.dtob"

    printf '%s' "$json" > "$infile"
    "$DTOB" encode "$infile" "$outfile" --types 2>/dev/null

    local got
    got=$("$DTOB" represent "$outfile" --flat 2>/dev/null | strip_ansi)

    if echo "$got" | grep -qF "$pattern"; then
        echo "  PASS: $name"
        PASS=$((PASS + 1))
    else
        echo "  FAIL: $name"
        echo "    expected to contain: $pattern"
        echo "    got: $got"
        FAIL=$((FAIL + 1))
    fi
}

# expect_fail: encoding should fail (non-zero exit)
expect_fail() {
    local name="$1" json="$2"
    local infile="$TMPDIR/in.json"
    local outfile="$TMPDIR/out.dtob"

    printf '%s' "$json" > "$infile"
    if "$DTOB" encode "$infile" "$outfile" --types 2>/dev/null; then
        echo "  FAIL: $name (expected failure but succeeded)"
        FAIL=$((FAIL + 1))
    else
        echo "  PASS: $name"
        PASS=$((PASS + 1))
    fi
}

echo "=== typed encode CLI tests ==="

# --- Enum tests ---
# Enums are nullable custom types (n_opcodes=0), shown as just the name

run_test "enum: single variant" \
    '{"types":{"color":["red","green","blue"]},"data":{"color":"red"}}' \
    '{ red }'

run_test "enum: different variant" \
    '{"types":{"color":["red","green","blue"]},"data":{"color":"blue"}}' \
    '{ blue }'

run_test "enum: multiple enum types" \
    '{"types":{"color":["red","green"],"size":["small","large"]},"data":{"color":"green","size":"large"}}' \
    '{ green , large }'

run_test "enum: with non-typed keys" \
    '{"types":{"color":["red","green"]},"data":{"color":"red","name":"test"}}' \
    '{ "name":"test", red }'

expect_fail "enum: invalid variant" \
    '{"types":{"color":["red","green"]},"data":{"color":"purple"}}'

# --- Enum: one-type ---
# Single-opcode enums don't show inner label (redundant)

run_test "enum: one-type int32" \
    '{"types":{"age":["int32"]},"data":{"age":30}}' \
    '{ age 30 }'

run_test_grep "enum: one-type double" \
    '{"types":{"temp":["double"]},"data":{"temp":3.14}}' \
    'temp 8 bytes'

run_test "enum: one-type uint8" \
    '{"types":{"level":["uint8"]},"data":{"level":5}}' \
    '{ level 5 }'

run_test "enum: one-type raw (string)" \
    '{"types":{"label":["raw"]},"data":{"label":"hello"}}' \
    '{ label "hello" }'

# --- Enum: multi-type ---

run_test "enum: multi-type (int32|raw) - int" \
    '{"types":{"val":["int32","raw"]},"data":{"val":42}}' \
    '{ val:int32 42 }'

run_test "enum: multi-type (int32|raw) - string" \
    '{"types":{"val":["int32","raw"]},"data":{"val":"hello"}}' \
    '{ val:raw "hello" }'

run_test "enum: multi-type (int32|uint64) - int" \
    '{"types":{"count":["int32","uint64"]},"data":{"count":100}}' \
    '{ count:int32 100 }'

# --- Mixed: enum types ---

run_test_grep "mixed: enum + normal keys" \
    '{"types":{"age":["int32"]},"data":{"age":30,"name":"test","count":5}}' \
    'age'

run_test "mixed: multiple enums" \
    '{"types":{"a":["int32"],"b":["uint8"]},"data":{"a":1,"b":2}}' \
    '{ a 1, b 2 }'

# --- Error cases ---

expect_fail "error: empty type array" \
    '{"types":{"x":[]},"data":{}}'

expect_fail "error: type value not array" \
    '{"types":{"x":"int32"},"data":{}}'

expect_fail "error: enum type mismatch (string for int32)" \
    '{"types":{"age":["int32"]},"data":{"age":"old"}}'

# --- Summary ---

echo ""
echo "$PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]
