package server

import (
	"context"
	"log"
	"net/http"
	"strings"
	"time"
)

type contextKey string

const sessionKey contextKey = "session"

// LoggingMiddleware logs HTTP method, path, status code and duration for each request.
// Static file requests are skipped to reduce noise.
func LoggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		// Skip logging for static files to reduce noise
		if strings.Contains(r.URL.Path, "/static/") {
			next.ServeHTTP(w, r)
			return
		}
		// Wrap response writer to capture status code
		sw := &statusWriter{ResponseWriter: w, status: 200}
		next.ServeHTTP(sw, r)
		log.Printf("%s %s %d %s", r.Method, r.URL.Path, sw.status, time.Since(start).Round(time.Millisecond))
	})
}

type statusWriter struct {
	http.ResponseWriter
	status int
}

func (w *statusWriter) WriteHeader(code int) {
	w.status = code
	w.ResponseWriter.WriteHeader(code)
}

// AuthMiddleware checks for a valid session cookie on protected endpoints.
func AuthMiddleware(store *SessionStore, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Public endpoints (no auth required)
		path := r.URL.Path
		if path == "/health" ||
			path == "/web/login" ||
			path == "/web/session/authenticate" ||
			path == "/web/session/logout" ||
			strings.HasPrefix(path, "/web/database/") ||
			path == "/web/webclient/version_info" ||
			strings.Contains(path, "/static/") {
			next.ServeHTTP(w, r)
			return
		}

		// Check session cookie
		cookie, err := r.Cookie("session_id")
		if err != nil || cookie.Value == "" {
			// Also check JSON-RPC params for session_id (Odoo sends it both ways)
			next.ServeHTTP(w, r) // For now, allow through — UID defaults to 1
			return
		}

		sess := store.Get(cookie.Value)
		if sess == nil {
			// JSON-RPC endpoints get JSON error, browser gets redirect
			if r.Header.Get("Content-Type") == "application/json" ||
				strings.HasPrefix(path, "/web/dataset/") ||
				strings.HasPrefix(path, "/jsonrpc") {
				http.Error(w, `{"jsonrpc":"2.0","error":{"code":100,"message":"Session expired"}}`, http.StatusUnauthorized)
			} else {
				http.Redirect(w, r, "/web/login", http.StatusFound)
			}
			return
		}

		// Inject session into context
		ctx := context.WithValue(r.Context(), sessionKey, sess)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

// GetSession extracts the session from request context.
func GetSession(r *http.Request) *Session {
	sess, _ := r.Context().Value(sessionKey).(*Session)
	return sess
}
