// Copyright 2025 Woodpecker Authors.
// Copyright 2024 The Gitea Authors.
//
// Licensed under the MIT License.

package optional

import "reflect"

type Option[T any] []T

func None[T any]() Option[T] {
	return nil
}

func Some[T any](v T) Option[T] {
	return Option[T]{v}
}

func FromPtr[T any](v *T) Option[T] {
	if v == nil {
		return None[T]()
	}
	return Some(*v)
}

func FromNonDefault[T comparable](v T) Option[T] {
	var zero T
	if v == zero {
		return None[T]()
	}
	return Some(v)
}

func (o Option[T]) Has() bool {
	return o != nil
}

func (o Option[T]) Value() T {
	var zero T
	return o.ValueOrDefault(zero)
}

func (o Option[T]) ValueOrDefault(v T) T {
	if o.Has() {
		return o[0]
	}
	return v
}

func (o Option[T]) ToPtr() *T {
	if o.Has() {
		return &o[0]
	}
	return nil
}

// ExtractValue return value or nil and bool if object was an Optional
// it should only be used if you already have to deal with interface{} values
// and expect an Option type within it.
func ExtractValue(obj any) (any, bool) {
	rt := reflect.TypeOf(obj)
	if rt.Kind() != reflect.Slice {
		return nil, false
	}

	type hasHasFunc interface {
		Has() bool
	}
	if hasObj, ok := obj.(hasHasFunc); !ok {
		return nil, false
	} else if !hasObj.Has() {
		return nil, true
	}

	rv := reflect.ValueOf(obj)
	if rv.Len() != 1 {
		// it's still false as optional.Option[T] types would have reported with hasObj.Has() that it is empty
		return nil, false
	}
	return rv.Index(0).Interface(), true
}
