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

// builtin-os-file.go
package expr

import (
	"fmt"
	"io"
	"slices"

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

const paramHandleOrPath = "handle-or-path"
const fileReadTextIteratorType = "fileReadTextIterator"

type fileReadTextIterator struct {
	osReader  *osReader
	index     int64
	count     int64
	line      string
	autoClose bool
}

func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator {
	return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose}
}

func (it *fileReadTextIterator) TypeName() string {
	return fileReadTextIteratorType
}

func (it *fileReadTextIterator) String() string {
	if it.osReader != nil && it.osReader.fh != nil {
		return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name())
	}
	return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
}

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

func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item
	if it.osReader.fh != nil {
		if it.line, err = it.osReader.reader.ReadString('\n'); err == nil {
			it.index++
			it.count++
			item = it.line[0 : len(it.line)-1]
		} else if it.autoClose {
			it.Clean()
		}
	}
	return
}

func (it *fileReadTextIterator) Current() (item any, err error) {
	if len(it.line) > 0 {
		item = it.line[0 : len(it.line)-1]
	}
	return
}

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

func (it *fileReadTextIterator) Reset() (err error) {
	if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil {
		it.index = -1
		it.count = 0
		it.line = ""
	}
	return
}

func (it *fileReadTextIterator) HasOperation(name string) bool {
	return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
}

func (it *fileReadTextIterator) Clean() (err error) {
	if it.osReader.fh != nil {
		if err = it.osReader.fh.Close(); err == nil {
			it.osReader = nil
		}
	}
	return nil
}

func (it *fileReadTextIterator) 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 fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
	var handle *osReader
	var invalidFileHandle any
	var ok, autoClose bool

	result = nil
	if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
		if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
			var handleAny any
			if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
				return
			}
			if handleAny != nil {
				handle = handleAny.(*osReader)
				autoClose = true
			}
		} else {
			invalidFileHandle = args[paramHandleOrPath]
		}
	}

	if handle != nil {
		result = newReadTextIterator(handle, autoClose)

	}

	if err == nil && (handle == nil || invalidFileHandle != nil) {
		err = errInvalidFileHandle(name, invalidFileHandle)
	}
	return
}

// func ImportOsIterFuncs(ctx ExprContext) {
// 	ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
// 		NewFuncParam(paramHandleOrPath),
// 	})
// }

// func init() {
// 	RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions")
// }
