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

// t_iterator_test.go
package expr

import (
	"testing"

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

func TestIteratorParser(t *testing.T) {
	section := "Iterator"
	inputs := []inputType{
		/*   1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); *it`, int64(0), nil},
		/*   2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
		/*   3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
		/*   4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; *it`, int64(0), nil},
		/*   5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
		/*   6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
		/*   7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
		/*   8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
		/*   9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, nil, nil},
		/*  10 */ {`it=$(1,2,3); it++`, int64(1), nil},
		/*  11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
		/*  12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
		/*  13 */ {`it=$([1,2,3,4],1,3); it++; it++;`, int64(3), nil},
		/*  14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
		/*  15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
		/*  16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
		/*  17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
		/*  18 */ {`it=$({"next":func(){5}}); it.clean`, nil, nil},
		/*  19 */ {`it=$({1:"one",2:"two",3:"three"}); it++`, int64(1), nil},
		/*  20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
		/*  21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
		/*  22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, array.NewArray([]any{int64(1), "one"}), nil},
		/*  23 */ {`$$($(1..4..0))`, nil, `[1:11] invalid interval specification: step must be positive`},
		/*  24 */ {`$$($(1..4..-1))`, nil, `[1:11] invalid interval specification: step must be positive`},
		/*  25 */ {`$$($(4..1..1))`, list.NewLinkedListA(4, 3, 2), nil},
		/*  26 */ {`it=$(1..5..2); it++`, int64(1), nil},
		/*  27 */ {`it=$(1.5..5..2); it++`, nil, `[1:13] interval expression expected integer, got float (1.5)`},
		/*  28 */ {`it=$(.."z"); it++`, nil, `[1:7] interval expression expected integer, got string (z)`},
		/*  29 */ {`it=$(3..1); it++; it.index`, int64(0), nil},
	}

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

func TestCallOpIntIter(t *testing.T) {
	section := "IntIterator-CallOp"

	if it, err := NewIntIteratorA(int64(1), int64(4), int64(1)); err != nil {
		t.Errorf(`%s -- NewIntIteratorA() failed: %v`, section, err)
	} else {
		testIteratorCallOp(t, section, it)
		testIterAttrs(t, section, it, "IntIterator", "$(1..4..1)")
	}
}

func TestCallOpIterIter(t *testing.T) {
	section := "IterIterator-CallOp"

	ctx := NewSimpleStore()
	if inner, err := NewIntIteratorA(int64(1), int64(4), int64(1)); err == nil {
		if it, err := NewIterIter(inner, ctx, []*scan.Term{}); err != nil {
			t.Errorf(`%s -- NewIterIter() failed: %v`, section, err)
		} else {
			testIteratorCallOp(t, section, it)
			testIterAttrs(t, section, it, "IterIter", "$($(1..4..1))")
		}
	} else {
		t.Errorf(`%s -- can't create inner iterator: %v`, section, err)
	}
}

func TestCallOpDictIter(t *testing.T) {
	section := "DictIterator-CallOp"

	inner := dict.NewDict(map[any]any{"a": 1})
	if it, err := NewDictIterator(inner, nil); err != nil {
		t.Errorf(`%s -- NewIterIter() failed: %v`, section, err)
	} else {
		testIteratorCallOp(t, section, it)
		testIterAttrs(t, section, it, "DictIterator", "$({#1})")
	}
}

func TestCallOpLinkedListIter(t *testing.T) {
	section := "LinkedListIterator-CallOp"

	inner := list.NewLinkedListA(1, 2, 3)
	it := NewLinkedListIterator(inner, nil)
	testIteratorCallOp(t, section, it)

	// wanted := "$([<#3>])"
	// got := it.String()
	// if wanted != got {
	// 	t.Errorf(`%s -- LinkedListIterator.String() failed: expected %q, got %q`, section, wanted, got)
	// }

	// wanted = "LinkedListIterator"
	// got = it.TypeName()
	// if wanted != got {
	// 	t.Errorf(`%s -- LinkedListIterator.TypeName() failed: expected %q, got %q`, section, wanted, got)
	// }
	testIterAttrs(t, section, it, "LinkedListIterator", "$([<#3>])")
}

func testIterAttrs(t *testing.T, section string, it kern.Iterator, name, repr string) {
	wanted := repr
	got := it.String()
	if wanted != got {
		t.Errorf(`%s -- %s.String() failed: expected %q, got %q`, section, name, wanted, got)
	}

	wanted = name
	got = it.TypeName()
	if wanted != got {
		t.Errorf(`%s -- %s.TypeName() failed: expected %q, got %q`, section, name, wanted, got)
	}
}

func TestFilterIterator(t *testing.T) {
	section := "Iterator-Filter"
	inputs := []inputType{
		/*   1 */ {`$$([1,2,3] filter $_%2==0)`, list.NewLinkedListA(2), nil},
		/*   2 */ {`it = [1,2,3] filter $_%2!=1; $$(it)`, list.NewLinkedListA(2), nil},
	}

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

func TestDigestIterator(t *testing.T) {
	section := "Iterator-Digest"
	inputs := []inputType{
		/*   1 */ {`$(..10) digest ${_}`, int64(9), nil},
		/*   2 */ {`$(10..0) digest ${_}`, int64(1), nil},
		/*   3 */ {`$(10..0..2) digest ${_}`, int64(2), nil},
	}

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

func TestCatIterator(t *testing.T) {
	section := "Iterator-Cat"
	inputs := []inputType{
		/*   1 */ {`$$([1] cat [])`, list.NewLinkedListA(1), nil},
		/*   2 */ {`$$([1] cat [2])`, list.NewLinkedListA(1, 2), nil},
		/*   3 */ {`$$([1] cat nil)`, list.NewLinkedListA(1), nil},
		/*   4 */ {`$$(nil cat [2])`, list.NewLinkedListA(2), nil},
		/*   5 */ {`$$(nil cat nil)`, list.NewLinkedListA(), nil},
		/*   6 */ {`$$(["a","b"] cat ["x"-true])`, nil, `[1:23] left operand 'x' [string] and right operand 'true' [bool] are not compatible with operator "-"`},
	}

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

func TestMapIterator(t *testing.T) {
	section := "Iterator-Map"
	inputs := []inputType{
		/*   1 */ {`$$([3,4,5] map ${_#})`, list.NewLinkedListA(1, 2, 3), nil},
		/*   2 */ {`#$$($(..10) map ${_})`, int64(10), nil},
		/*   3 */ {`#$$($(10..0) map ${_})`, int64(10), nil},
		/*   4 */ {`$$(["1", "2", "3"] map int())`, nil, `int(): too few params -- expected 1, got 0`},
	}

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