// Package headers Middleware based on https://github.com/unrolled/secure.
package headers

import (
	"context"
	"errors"
	"net/http"

	"github.com/opentracing/opentracing-go/ext"
	"github.com/traefik/traefik/v2/pkg/config/dynamic"
	"github.com/traefik/traefik/v2/pkg/log"
	"github.com/traefik/traefik/v2/pkg/middlewares"
	"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
	"github.com/traefik/traefik/v2/pkg/tracing"
)

const (
	typeName = "Headers"
)

func handleDeprecation(ctx context.Context, cfg *dynamic.Headers) {
	if cfg.SSLRedirect {
		log.FromContext(ctx).Warn("SSLRedirect is deprecated, please use entrypoint redirection instead.")
	}
	if cfg.SSLTemporaryRedirect {
		log.FromContext(ctx).Warn("SSLTemporaryRedirect is deprecated, please use entrypoint redirection instead.")
	}
	if cfg.SSLHost != "" {
		log.FromContext(ctx).Warn("SSLHost is deprecated, please use RedirectRegex middleware instead.")
	}
	if cfg.SSLForceHost {
		log.FromContext(ctx).Warn("SSLForceHost is deprecated, please use RedirectScheme middleware instead.")
	}
	if cfg.FeaturePolicy != "" {
		log.FromContext(ctx).Warn("FeaturePolicy is deprecated, please use PermissionsPolicy header instead.")
	}
}

type headers struct {
	name    string
	handler http.Handler
}

// New creates a Headers middleware.
func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name string) (http.Handler, error) {
	// HeaderMiddleware -> SecureMiddleWare -> next
	mCtx := middlewares.GetLoggerCtx(ctx, name, typeName)
	logger := log.FromContext(mCtx)
	logger.Debug("Creating middleware")

	handleDeprecation(mCtx, &cfg)

	hasSecureHeaders := cfg.HasSecureHeadersDefined()
	hasCustomHeaders := cfg.HasCustomHeadersDefined()
	hasCorsHeaders := cfg.HasCorsHeadersDefined()

	if !hasSecureHeaders && !hasCustomHeaders && !hasCorsHeaders {
		return nil, errors.New("headers configuration not valid")
	}

	var handler http.Handler
	nextHandler := next

	if hasSecureHeaders {
		logger.Debugf("Setting up secureHeaders from %v", cfg)
		handler = newSecure(next, cfg, name)
		nextHandler = handler
	}

	if hasCustomHeaders || hasCorsHeaders {
		logger.Debugf("Setting up customHeaders/Cors from %v", cfg)
		h, err := NewHeader(nextHandler, cfg)
		if err != nil {
			return nil, err
		}

		handler = connectionheader.Remover(h)
	}

	return &headers{
		handler: handler,
		name:    name,
	}, nil
}

func (h *headers) GetTracingInformation() (string, ext.SpanKindEnum) {
	return h.name, tracing.SpanKindNoneEnum
}

func (h *headers) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	h.handler.ServeHTTP(rw, req)
}
