package svc

import (
	"errors"
	"fmt"
	"reflect"
	"strings"

	"git.simanc.com/software/golib/v4/features/mo"
	"git.simanc.com/software/golib/v4/infra/ii"
)

func (s *Service) resolveInsert(value any) (mo.D, error) {
	switch val := value.(type) {
	case mo.D:
		return val, nil
	case mo.M:
		var doc mo.D
		return doc, mo.Decode(val, &doc)
	case Row:
		return val.D, nil
	default:
		var doc mo.D
		if err := mo.DecodeJson(val, &doc); err != nil {
			return nil, err
		}
		return doc, nil
	}
}

func (s *Service) toDocs(value any, f func(doc mo.D) (mo.D, error)) (mo.A, error) {
	dv := reflect.ValueOf(value)
	if dv.Kind() != reflect.Slice {
		return nil, ErrDataTypeError
	}
	if dv.Len() == 0 {
		return nil, ErrDataError
	}
	docs := make(mo.A, dv.Len())
	for i := 0; i < dv.Len(); i++ {
		params, err := s.resolveInsert(dv.Index(i).Interface())
		if err != nil {
			return nil, err
		}
		doc, err := f(params)
		if err != nil {
			s.Log.Error("svc.toDocs: the %d element handled: %s", i, err)
			return nil, err
		}
		docs[i] = doc
	}
	return docs, nil
}

func (s *Service) checkBindType(v any) error {
	if reflect.TypeOf(v).Kind() != reflect.Ptr {
		return ErrBindTypeError
	}
	return nil
}

func splitPATH(path, prefix string) (string, ii.Name, error) {
	// "","item","insertOne","test.user"
	pathList := strings.Split(path, "/")
	if len(pathList) != 4 {
		return "", ii.Name{}, fmt.Errorf("err path: %s", path)
	}
	if pathList[1] != prefix {
		return "", ii.Name{}, errors.New("the first element of PATH must be: item")
	}
	name, err := ii.NewNameErr(pathList[3])
	if err != nil {
		return "", ii.Name{}, err
	}
	return pathList[2], name, nil
}

func Decode(row Row, v any) error {
	return mo.Decode(row.Clone(), v)
}

func DecodeAll[T any](rows []Row, dst *[]T) error {
	if len(*dst) < len(rows) {
		*dst = make([]T, len(rows))
	}
	for i, row := range rows {
		var v T
		if err := Decode(row, &v); err != nil {
			return err
		}
		(*dst)[i] = v
	}
	return nil
}

func Unmarshal(b []byte) (Row, error) {
	var raw mo.D
	if err := mo.UnmarshalExtJSON(b, true, &raw); err != nil {
		return Row{}, err
	}
	return Row{D: raw}, nil
}
