package apidsl

import (
	"github.com/goadesign/goa/design"
	"github.com/goadesign/goa/dslengine"
)

// Metadata is a set of key/value pairs that can be assigned to an object. Each value consists of a
// slice of strings so that multiple invocation of the Metadata function on the same target using
// the same key builds up the slice. Metadata may be set on attributes, media types, actions,
// responses, resources and API definitions.
//
// While keys can have any value the following names are handled explicitly by goagen when set on
// attributes.
//
// `struct:field:name`: overrides the Go struct field name generated by default by goagen.
// Applicable to attributes only.
//
//        Metadata("struct:field:name", "MyName")
//
// `struct:tag:xxx`: sets the struct field tag xxx on generated Go structs.  Overrides tags that
// goagen would otherwise set.  If the metadata value is a slice then the strings are joined with
// the space character as separator.
// Applicable to attributes only.
//
//        Metadata("struct:tag:json", "myName,omitempty")
//        Metadata("struct:tag:xml", "myName,attr")
//
// `swagger:generate`: specifies whether Swagger specification should be generated. Defaults to
// true.
// Applicable to resources, actions and file servers.
//
//        Metadata("swagger:generate", "false")
//
// `swagger:summary`: sets the Swagger operation summary field.
// Applicable to actions.
//
//        Metadata("swagger:summary", "Short summary of what action does")
//
// `swagger:tag:xxx`: sets the Swagger object field tag xxx.
// Applicable to resources and actions.
//
//        Metadata("swagger:tag:Backend")
//        Metadata("swagger:tag:Backend:desc", "Quick description of what 'Backend' is")
//        Metadata("swagger:tag:Backend:url", "http://example.com")
//        Metadata("swagger:tag:Backend:url:desc", "See more docs here")
//
// `swagger:extension:xxx`: sets the Swagger extensions xxx. It can have any valid JSON format value.
// Applicable to
// api as within the info and tag object,
// resource as within the paths object,
// action as within the path-item object,
// route as within the operation object,
// param as within the parameter object,
// response as within the response object
// and security as within the security-scheme object.
// See https://github.com/OAI/OpenAPI-Specification/blob/master/guidelines/EXTENSIONS.md.
//
//        Metadata("swagger:extension:x-api", `{"foo":"bar"}`)
//
// The special key names listed above may be used as follows:
//
//        var Account = Type("Account", func() {
//                Attribute("service", String, "Name of service", func() {
//                        // Override default name to avoid clash with built-in 'Service' field.
//                        Metadata("struct:field:name", "ServiceName")
//                })
//        })
//
func Metadata(name string, value ...string) {
	appendMetadata := func(metadata dslengine.MetadataDefinition, name string, value ...string) dslengine.MetadataDefinition {
		if metadata == nil {
			metadata = make(map[string][]string)
		}
		metadata[name] = append(metadata[name], value...)
		return metadata
	}

	switch def := dslengine.CurrentDefinition().(type) {
	case design.ContainerDefinition:
		att := def.Attribute()
		att.Metadata = appendMetadata(att.Metadata, name, value...)
	case *design.AttributeDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.MediaTypeDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.ActionDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.FileServerDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.ResourceDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.ResponseDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.APIDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.RouteDefinition:
		def.Metadata = appendMetadata(def.Metadata, name, value...)
	case *design.SecurityDefinition:
		def.Scheme.Metadata = appendMetadata(def.Scheme.Metadata, name, value...)
	default:
		dslengine.IncompatibleDSL()
	}
}
