// Package httpapi exposes the photo-api HTTP surface under /api/photos,
// following the backend's route and response conventions (resource-keyed
// success bodies, {"error": string} failures, Bearer auth).
package httpapi

import (
	"log"
	"net/http"
	"time"

	"git.azzamo.net/Michilis/Spanglish/photo-api/internal/auth"
	"git.azzamo.net/Michilis/Spanglish/photo-api/internal/config"
	"git.azzamo.net/Michilis/Spanglish/photo-api/internal/storage"
	"git.azzamo.net/Michilis/Spanglish/photo-api/internal/store"
	"git.azzamo.net/Michilis/Spanglish/photo-api/internal/worker"
)

type Server struct {
	cfg      config.Config
	db       *store.DB
	storage  storage.Storage
	verifier *auth.Verifier
	worker   *worker.Worker
}

func New(cfg config.Config, db *store.DB, st storage.Storage, verifier *auth.Verifier, w *worker.Worker) *Server {
	return &Server{cfg: cfg, db: db, storage: st, verifier: verifier, worker: w}
}

func (s *Server) Handler() http.Handler {
	mux := http.NewServeMux()

	health := func(w http.ResponseWriter, _ *http.Request) {
		writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
	}
	mux.HandleFunc("GET /health", health)
	mux.HandleFunc("GET /api/photos/health", health)

	// Admin surface: role admin|organizer only (mirrors /api/media).
	mux.HandleFunc("POST /api/photos/galleries", s.requireAdmin(s.createGallery))
	mux.HandleFunc("GET /api/photos/galleries", s.requireAdmin(s.listGalleries))
	mux.HandleFunc("GET /api/photos/galleries/{id}", s.requireAdmin(s.getGallery))
	mux.HandleFunc("PATCH /api/photos/galleries/{id}", s.requireAdmin(s.updateGallery))
	mux.HandleFunc("DELETE /api/photos/galleries/{id}", s.requireAdmin(s.deleteGallery))
	mux.HandleFunc("POST /api/photos/galleries/{id}/photos", s.requireAdmin(s.uploadPhotos))
	mux.HandleFunc("PATCH /api/photos/galleries/{id}/order", s.requireAdmin(s.reorderPhotos))
	mux.HandleFunc("POST /api/photos/galleries/{id}/share-token", s.requireAdmin(s.rotateShareToken))
	mux.HandleFunc("DELETE /api/photos/photos/{photoId}", s.requireAdmin(s.deletePhoto))
	mux.HandleFunc("POST /api/photos/photos/{photoId}/retry", s.requireAdmin(s.retryPhoto))

	// Viewer surface: optional auth, checked per gallery visibility.
	mux.HandleFunc("GET /api/photos/public/galleries", s.listPublicGalleries)
	mux.HandleFunc("GET /api/photos/public/galleries/{slug}", s.getPublicGallery)
	mux.HandleFunc("GET /api/photos/public/events/{eventSlug}/gallery", s.getEventGallery)
	// The literal "download" segment takes precedence over {variant} under
	// ServeMux's specificity rules, so the two can share the prefix.
	mux.HandleFunc("GET /api/photos/files/{photoId}/download", s.serveDownload)
	mux.HandleFunc("GET /api/photos/files/{photoId}/{variant}", s.serveFile)

	return s.withCommon(mux)
}

// withCommon adds request logging and a same-spirit CORS allowance as the
// backend's cors() (origin = FRONTEND_URL); in production nginx serves
// same-origin so this mostly matters in development.
func (s *Server) withCommon(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if origin := r.Header.Get("Origin"); origin != "" && origin == s.cfg.FrontendURL {
			w.Header().Set("Access-Control-Allow-Origin", origin)
			w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PATCH, DELETE, OPTIONS")
			w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
			w.Header().Set("Vary", "Origin")
		}
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		start := time.Now()
		sw := &statusWriter{ResponseWriter: w, status: http.StatusOK}
		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)
}
