package httpmw

import (
	"fmt"
	"net/http"
	"strings"

	"golang.org/x/xerrors"
)

const (
	hstsHeader = "Strict-Transport-Security"
)

type HSTSConfig struct {
	// HeaderValue is an empty string if hsts header is disabled.
	HeaderValue string
}

func HSTSConfigOptions(maxAge int, options []string) (HSTSConfig, error) {
	if maxAge <= 0 {
		// No header, so no need to build the header string.
		return HSTSConfig{HeaderValue: ""}, nil
	}

	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
	var str strings.Builder
	_, err := str.WriteString(fmt.Sprintf("max-age=%d", maxAge))
	if err != nil {
		return HSTSConfig{}, xerrors.Errorf("hsts: write max-age: %w", err)
	}

	for _, option := range options {
		switch {
		// Only allow valid options and fix any casing mistakes
		case strings.EqualFold(option, "includeSubDomains"):
			option = "includeSubDomains"
		case strings.EqualFold(option, "preload"):
			option = "preload"
		default:
			return HSTSConfig{}, xerrors.Errorf("hsts: invalid option: %q. Must be 'preload' and/or 'includeSubDomains'", option)
		}
		_, err = str.WriteString("; " + option)
		if err != nil {
			return HSTSConfig{}, xerrors.Errorf("hsts: write option: %w", err)
		}
	}
	return HSTSConfig{
		HeaderValue: str.String(),
	}, nil
}

// HSTS will add the strict-transport-security header if enabled. This header
// forces a browser to always use https for the domain after it loads https once.
// Meaning: On first load of product.coder.com, they are redirected to https. On
// all subsequent loads, the client's local browser forces https. This prevents
// man in the middle.
//
// This header only makes sense if the app is using tls.
//
// Full header example:
// Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
func HSTS(next http.Handler, cfg HSTSConfig) http.Handler {
	if cfg.HeaderValue == "" {
		// No header, so no need to wrap the handler.
		return next
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set(hstsHeader, cfg.HeaderValue)
		next.ServeHTTP(w, r)
	})
}
