// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.

// t_dict_test.go
package expr

import (
	"errors"

	"testing"

	"git.portale-stac.it/go-pkg/expr/kern"
	"git.portale-stac.it/go-pkg/expr/scan"
	"git.portale-stac.it/go-pkg/expr/sym"
	"git.portale-stac.it/go-pkg/expr/types/dict"
	"git.portale-stac.it/go-pkg/expr/types/list"
)

func TestDictParser(t *testing.T) {
	section := "Dict"

	inputs := []inputType{
		/*  1 */ {`{}`, dict.NewDict(nil), nil},
		/*  2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
		/*  3 */ {`{1:"one",2:"two",3:"three"}`, dict.NewDict(map[any]any{int64(1): "one", int64(2): "two", int64(3): "three"}), nil},
		/*  4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
		/*  5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
		/*  6 */ {`{1:"one"} + {2:"two"}`, dict.NewDict(map[any]any{int64(1): "one", int64(2): "two"}), nil},
		/*  7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
		/*  8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, dict.NewDict(map[any]any{"a": int64(9), "b": int64(2)}), nil},
		/*  9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, dict.NewDict(map[any]any{"z": int64(9), "a": int64(1), "b": int64(2)}), nil},
		/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
		/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
		/* 12 */ {`m={
			"a":1,
			//"b":2,
			"c":3
			}`, dict.NewDict(map[any]any{"a": int64(1), "c": int64(3)}), nil},
		/* 13 */ {`D={"a":1, "b":2}; D."a"`, int64(1), nil},
		/* 14 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil},
		/* 15 */ {`D={1:"a", 2:"b", 3:"c"}; D.(1+2)`, "c", nil},
		/* 16 */ {`D={1:"a"}; D.2`, nil, `[1:15] key 2 not found`},
		/* 17 */ {`D={1:"a"}; D."2"`, nil, `[1:17] key "2" not found`},
		/* 18 */ {`D={"a":1, "b":2}; D.a = 10; D.a`, int64(10), nil},
		/* 19 */ {`k="a"; D={k: 10}`, dict.NewDict(map[any]any{"a": int64(10)}), nil},
		/* 20 */ {`k=[<1,2>]; D={k: 10}`, nil, `[1:16] dict key can be integer or string, got lisked-list`},
		/* 21 */ {`f=func(n){"x"+n}; d{f(5):10}`, nil, `[0:0] two adjacent operators: "d" and "{}"`},
		/* 22 */ {`f=func(n){"x"+n}; d={f(5):10}`, dict.NewDict(map[any]any{"x5": int64(10)}), nil},
		/* 23 */ {`f=func(n){"x"+n}; d={f(5); "z":10}`, nil, "[1:27] expected one of  `:`, `}`, got `;`"},
		/* 24 */ {`f=func(n){"x"+n}; d={f(5) but "z":10}`, dict.NewDict(map[any]any{"z": int64(10)}), nil},
		/* 25 */ {`3 in {1:"one", 2:"two"}`, false, nil},
		/* 26 */ {`1 at {"a":2, "b":3, "c":1}`, "c", nil},
		/* 27 */ {`10 at {"a":2, "b":3, "c":1}`, nil, nil},
	}

	// RunTestSuiteSpec(t, section, inputs, 1)
	RunTestSuite(t, section, inputs)
}

func TestOperations(t *testing.T) {
	section := "Dict-Operations"
	inputs := []inputType{
		/*   1 */ {`{"a":1} + {"b":2}`, dict.NewDict(map[any]any{"a": int64(1), "b": int64(2)}), nil},
		/*   2 */ {`D={"a":1}; C={"b":2}; X=D+C; C."b" = 3; X`, dict.NewDict(map[any]any{"a": int64(1), "b": int64(2)}), nil},
		/*   3 */ {`D={"a":1}; C={"b":2}; X=D+C; D."a" = 3; X`, dict.NewDict(map[any]any{"a": int64(1), "b": int64(2)}), nil},
		/*   4 */ {`L=[8,9]; D={"a":L}; L[0]=11; D."a"[0]`, int64(11), nil},
		/*   5 */ {`L=[8,9]; D:={"a":L}; L[0]=11; D."a"[0]`, int64(8), nil},
	}
	// runTestSuiteSpec(t, section, inputs, 1)
	RunTestSuite(t, section, inputs)
}

func TestAccessSubFields(t *testing.T) {
	section := "Dict-Assign-Item"
	ctx := NewSimpleStore()
	ctx.UnsafeSetVar(
		"D",
		dict.NewDict(map[any]any{
			"a": dict.NewDict(map[any]any{"uno": int64(10)}),
			"b": list.NewLinkedListA(1, 2, 3),
		}),
	) // D={"a": {"uno":1}}

	inputs := []inputType{
		/* 1 */ {`D.a.uno`, int64(10), nil},
		/* 2 */ {`D.a.uno = 111; D.a."uno"`, int64(111), nil},
		/* 3 */ {`D.a.uno = 111; D["a"]["uno"]`, int64(111), nil},
		/* 4 */ {`D.b[1] = 22; D["b"][1]`, int64(22), nil},
		// /* 4 */ {`D.b[1] = 22; D["b"][1]`, nil, `[1:3] collection expected`},
	}
	RunCtxTestSuiteSpec(t, ctx, section, inputs, 4)
	// runCtxTestSuite(t, ctx, section, inputs)
}

func TestDictDeepAssign(t *testing.T) {
	section := "Dict-DeepAssign"
	inputs := []inputType{
		/*   1 */ {`DD={"uno": 1, "due": 2}; D=DD; D["uno"]=11; DD`, dict.NewDict(map[any]any{"uno": int64(11), "due": int64(2)}), nil},
		/*   2 */ {`DD={"uno": 1, "due": 2}; D:=DD; D["uno"]=11; DD`, dict.NewDict(map[any]any{"uno": int64(1), "due": int64(2)}), nil},
	}

	// runTestSuiteSpec(t, section, inputs, 4)
	RunTestSuite(t, section, inputs)
}

func TestDictToStringMultiLine(t *testing.T) {
	var good bool
	section := "dict-ToString-ML"
	want := `{
  "first": 1
}`
	args := map[any]any{
		"first": newLiteralTerm(scan.NewValueToken(0, 0, sym.SymInteger, "1", 1)),
	}
	dict := dict.NewDict(args)
	got := dict.ToString(kern.MultiLine)
	// fmt.Printf("got=%q\n", got)

	if good = got == want; !good {
		t.Errorf("ToString(MultiLine): got = %q, want %q", got, want)
	}

	if good {
		t.Logf("%s -- succeeded", section)
	} else {
		t.Logf("%s -- failed", section)
	}
}

func TestDictToString(t *testing.T) {
	var good bool
	section := "dict-ToString-SL"
	want := `{"first": 1}`
	args := map[any]any{
		"first": newLiteralTerm(scan.NewValueToken(0, 0, sym.SymInteger, "1", 1)),
	}
	dict := dict.NewDict(args)
	got := dict.ToString(0)
	// fmt.Printf("got=%q\n", got)

	if good = got == want; !good {
		t.Errorf("ToString(0): got = %q, want %q", got, want)
	}

	if good {
		t.Logf("%s -- succeeded", section)
	} else {
		t.Logf("%s -- failed", section)
	}
}
