package middleware

import (
	"encoding/json"
	"net/http"
	"strings"
)

// CorsMiddleware — browser CORS for Harbor Desk FE.
// Note: cannot combine Access-Control-Allow-Origin: * with Allow-Credentials: true
// (browsers reject). FE uses Bearer token (no cookies), so no credentials header.
type CorsMiddleware struct {
	allowedOrigins map[string]struct{}
}

func NewCorsMiddleware(origins []string) *CorsMiddleware {
	allowed := make(map[string]struct{}, len(origins))
	for _, origin := range origins {
		origin = strings.TrimRight(strings.TrimSpace(origin), "/")
		if origin != "" {
			allowed[origin] = struct{}{}
		}
	}
	return &CorsMiddleware{allowedOrigins: allowed}
}

func (m *CorsMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !m.setHeader(w, r) {
			writeOriginDenied(w)
			return
		}
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		next(w, r)
	}
}

func (m *CorsMiddleware) Handler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !m.setHeader(w, r) {
			writeOriginDenied(w)
			return
		}
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		w.WriteHeader(http.StatusNotFound)
	})
}

func writeOriginDenied(w http.ResponseWriter) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusForbidden)
	_ = json.NewEncoder(w).Encode(map[string]any{
		"code": 403003, "message": "origin is not allowed", "data": nil, "error": nil,
	})
}

func (m *CorsMiddleware) setHeader(w http.ResponseWriter, r *http.Request) bool {
	origin := strings.TrimRight(strings.TrimSpace(r.Header.Get("Origin")), "/")
	if origin != "" {
		if _, ok := m.allowedOrigins[origin]; !ok {
			return false
		}
		w.Header().Set("Access-Control-Allow-Origin", origin)
		appendVary(w, "Origin")
	}
	w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Request-Id, device_id")
	w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")
	w.Header().Set("Access-Control-Max-Age", "600")
	w.Header().Set("Access-Control-Expose-Headers", "Content-Length, Content-Type")
	return true
}

func appendVary(w http.ResponseWriter, value string) {
	for _, existing := range w.Header().Values("Vary") {
		for _, part := range strings.Split(existing, ",") {
			if strings.EqualFold(strings.TrimSpace(part), value) {
				return
			}
		}
	}
	w.Header().Add("Vary", value)
}
