package design

import (
	"errors"
	"fmt"
)

type Shipping func(items int) int
type options struct {
	limit    int
	shipping Shipping
}
type Option func(*options) error

func WithLimit(n int) Option {
	return func(o *options) error {
		if n <= 0 {
			return errors.New("limit must be positive")
		}
		o.limit = n
		return nil
	}
}
func WithShipping(s Shipping) Option {
	return func(o *options) error {
		if s == nil {
			return errors.New("shipping strategy is required")
		}
		o.shipping = s
		return nil
	}
}

type Order struct {
	items     int
	limit     int
	shipping  Shipping
	submitted bool
}

func NewOrder(opts ...Option) (*Order, error) {
	cfg := options{limit: 10, shipping: func(int) int { return 500 }}
	for _, opt := range opts {
		if opt == nil {
			return nil, errors.New("nil option")
		}
		if err := opt(&cfg); err != nil {
			return nil, err
		}
	}
	return &Order{limit: cfg.limit, shipping: cfg.shipping}, nil
}
func (o *Order) Add(n int) error {
	if o.submitted {
		return errors.New("order already submitted")
	}
	if n <= 0 || n > o.limit-o.items {
		return errors.New("item limit exceeded")
	}
	o.items += n
	return nil
}
func (o *Order) Submit() (int, error) {
	if o.submitted {
		return 0, errors.New("order already submitted")
	}
	if o.items == 0 {
		return 0, errors.New("empty order")
	}
	fee := o.shipping(o.items)
	if fee < 0 {
		return 0, errors.New("negative shipping fee")
	}
	o.submitted = true
	return fee, nil
}
func Strategy(name string) (Shipping, error) {
	switch name {
	case "pickup":
		return func(int) int { return 0 }, nil
	case "delivery":
		return func(n int) int { return 500 + n*10 }, nil
	default:
		return nil, fmt.Errorf("unknown shipping strategy %q", name)
	}
}
