// kafka — a privacy-respecting metasearch engine
// Copyright (C) 2026-present metamorphosis-dev
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

package middleware

import (
	"net/http"
	"strconv"
	"strings"
)

// CORSConfig controls Cross-Origin Resource Sharing headers.
type CORSConfig struct {
	// AllowedOrigins is a list of allowed origin patterns.
	// Use "*" to allow all origins, or specific domains like "https://example.com".
	AllowedOrigins []string
	// AllowedMethods defaults to GET, POST, OPTIONS if empty.
	AllowedMethods []string
	// AllowedHeaders defaults to Content-Type, Authorization if empty.
	AllowedHeaders []string
	// ExposedHeaders lists headers the browser can access from the response.
	ExposedHeaders []string
	// MaxAge is the preflight cache duration in seconds (default: 3600).
	MaxAge int
}

// CORS returns a middleware that sets CORS headers on all responses
// and handles OPTIONS preflight requests.
func CORS(cfg CORSConfig) func(http.Handler) http.Handler {
	origins := cfg.AllowedOrigins
	if len(origins) == 0 {
		// Default: no CORS headers. Explicitly configure origins to enable.
		origins = nil
	}

	methods := cfg.AllowedMethods
	if len(methods) == 0 {
		methods = []string{"GET", "POST", "OPTIONS"}
	}

	headers := cfg.AllowedHeaders
	if len(headers) == 0 {
		headers = []string{"Content-Type", "Authorization", "X-Search-Token", "X-Brave-Access-Token"}
	}

	maxAge := cfg.MaxAge
	if maxAge <= 0 {
		maxAge = 3600
	}

	methodsStr := strings.Join(methods, ", ")
	headersStr := strings.Join(headers, ", ")
	exposedStr := strings.Join(cfg.ExposedHeaders, ", ")
	maxAgeStr := strconv.Itoa(maxAge)

	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			origin := r.Header.Get("Origin")

			// Determine the allowed origin for this request.
			// If no origins are configured, CORS is disabled entirely — no headers are set.
			allowedOrigin := ""
			for _, o := range origins {
				if o == "*" {
					allowedOrigin = "*"
					break
				}
				if o == origin {
					allowedOrigin = origin
					break
				}
			}

			if allowedOrigin != "" {
				w.Header().Set("Access-Control-Allow-Origin", allowedOrigin)
				w.Header().Set("Access-Control-Allow-Methods", methodsStr)
				w.Header().Set("Access-Control-Allow-Headers", headersStr)
				if exposedStr != "" {
					w.Header().Set("Access-Control-Expose-Headers", exposedStr)
				}
				w.Header().Set("Access-Control-Max-Age", maxAgeStr)
			}

			// Handle preflight.
			if r.Method == http.MethodOptions {
				w.WriteHeader(http.StatusNoContent)
				return
			}

			next.ServeHTTP(w, r)
		})
	}
}
