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

// iter-iter.go
package expr

import (
	"fmt"
	"io"

	"git.portale-stac.it/go-pkg/expr/kern"
	"git.portale-stac.it/go-pkg/expr/scan"
)

const iterIterType = "IterIter"

type IterIter struct {
	it       kern.Iterator
	count    int64
	index    int64
	ctx      kern.ExprContext
	exprList []*scan.Term
	current  any
}

func NewIterIter(it kern.Iterator, ctx kern.ExprContext, exprs []*scan.Term) (iter kern.Iterator, err error) {
	if ctx == nil {
		err = fmt.Errorf("context is required for %s", iterIterType)
	} else if it == nil {
		err = fmt.Errorf("source iterator is required for %s", iterIterType)
	} else {
		iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, exprList: exprs, current: nil}
	}
	return
}

func (it *IterIter) String() string {
	return fmt.Sprintf("$(%s)", it.it)
}

func (it *IterIter) TypeName() string {
	return iterIterType
}

func (it *IterIter) HasOperation(name string) bool {
	return HasIterStandardOperations(name)
}

func (it *IterIter) CallOperation(name string, args map[string]any) (v any, err error) {
	switch name {
	case kern.NextName:
		v, err = it.Next()
	case kern.ResetName:
		err = it.Reset()
	case kern.CleanName:
		err = it.Clean()
	case kern.IndexName:
		v = int64(it.Index())
	case kern.CurrentName:
		v, err = it.Current()
	case kern.CountName:
		v = it.count
	default:
		err = kern.ErrNoOperation(name)
	}
	return
}

func (it *IterIter) Current() (item any, err error) {
	if it.current != nil {
		item = it.current
	} else if len(it.exprList) > 0 {
		// Evaluate the expression list and use the result as the current item
		var exprValue any
		for _, expr := range it.exprList {
			if exprValue, err = expr.Compute(it.ctx); err != nil {
				break
			}
			it.ctx.UnsafeSetVar(kern.ControlLastResult, exprValue)
		}
		if err == nil {
			item = exprValue
		}
	} else {
		var exists bool
		if it.current, exists = it.ctx.GetVar("_"); !exists {
			err = fmt.Errorf("current item not available")
		} else {
			item = it.current
		}
	}
	return
}

func (it *IterIter) Next() (item any, err error) {
	var src any

	it.current = nil
	ctx := it.ctx
	for src, err = it.it.Next(); src == nil && err == nil; src, err = it.it.Next() {
	}

	if err == nil {
		if src == nil {
			err = io.EOF
		} else {
			ctx.UnsafeSetVar("_", src)
			ctx.UnsafeSetVar("__", it.it.Index())
			ctx.UnsafeSetVar("_#", it.it.Count())
			item, err = it.Current()
			ctx.DeleteVar("_#")
			ctx.DeleteVar("__")
			ctx.DeleteVar("_")
		}
	}
	return
}

func (it *IterIter) Index() int64 {
	return it.index
}

func (it *IterIter) Count() int64 {
	return it.count
}

func (it *IterIter) Reset() error {
	it.index = -1
	it.count = 0
	return nil
}

func (it *IterIter) Clean() error {
	return nil
}
