package form

import (
	"github.com/pkg/errors"
)

type Attrs map[string]any

type FieldUnmarshaller interface {
	Unmarshal(v string) error
}

type ValidationFunc func(f *Field) error

type Field struct {
	name       string
	attrs      map[string]any
	validators []ValidationFunc
}

func (f *Field) Name() string {
	return f.name
}

func (f *Field) Get(key string) (value any, exists bool) {
	value, exists = f.attrs[key]
	return
}

func (f *Field) Set(key string, value any) {
	f.attrs[key] = value
}

func (f *Field) Del(key string) {
	delete(f.attrs, key)
}

func (f *Field) Attrs() map[string]any {
	return f.attrs
}

func (f *Field) Validate() error {
	for _, fn := range f.validators {
		if err := fn(f); err != nil {
			return errors.WithStack(err)
		}
	}
	return nil
}

func NewField(name string, attrs Attrs, validators ...ValidationFunc) *Field {
	return &Field{
		name:       name,
		attrs:      attrs,
		validators: validators,
	}
}

func FormFieldAttr[T any](f *Form, name string, attr string) (T, error) {
	field := f.Field(name)
	if field == nil {
		return *new(T), errors.WithStack(ErrFieldNotFound)
	}

	value, err := FieldAttr[T](field, attr)
	if err != nil {
		return *new(T), errors.WithStack(err)
	}

	return value, nil
}

func FieldAttr[T any](f *Field, name string) (T, error) {
	raw, ok := f.Get(name)
	if !ok {
		return *new(T), errors.WithStack(ErrMissingValue)
	}

	value, ok := raw.(T)
	if !ok {
		return *new(T), errors.WithStack(ErrUnexpectedValue)
	}

	return value, nil
}