package api

import (
	"fmt"
	"io"
	"net/http"
	"strconv"

	"archivmail/internal/audit"
)

// ── Logo handlers (admin: any tenant) ───────────────────────────────────────

func (s *Server) handleGetTenantLogo(w http.ResponseWriter, r *http.Request) {
	if s.tenantStore == nil {
		writeError(w, http.StatusServiceUnavailable, "tenant store not available")
		return
	}
	id, err := parseTenantID(r)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid tenant id")
		return
	}
	// Tenant-scope enforcement (IDOR fix, PROJ-61): non-global sessions may only
	// read their own tenant's logo. Global admins (sess.TenantID == nil) see all.
	sess := sessionFromCtx(r.Context())
	if !tenantAccessAllowed(sess, &id) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	data, contentType, err := s.tenantStore.GetLogo(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to load logo")
		return
	}
	if data == nil {
		writeError(w, http.StatusNotFound, "no logo set")
		return
	}
	if contentType == "" {
		contentType = "image/png"
	}
	w.Header().Set("Content-Type", contentType)
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.Header().Set("Cache-Control", "public, max-age=86400")
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(data)
}

func (s *Server) handleUploadTenantLogo(w http.ResponseWriter, r *http.Request) {
	if s.tenantStore == nil {
		writeError(w, http.StatusServiceUnavailable, "tenant store not available")
		return
	}
	id, err := parseTenantID(r)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid tenant id")
		return
	}
	// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
	if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	s.saveTenantLogo(w, r, id)
}

func (s *Server) handleDeleteTenantLogo(w http.ResponseWriter, r *http.Request) {
	if s.tenantStore == nil {
		writeError(w, http.StatusServiceUnavailable, "tenant store not available")
		return
	}
	id, err := parseTenantID(r)
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid tenant id")
		return
	}
	// Defense-in-depth tenant scope check (PROJ-63): no-op for global admins.
	if !tenantAccessAllowed(sessionFromCtx(r.Context()), &id) {
		writeError(w, http.StatusForbidden, "access denied")
		return
	}
	if err := s.tenantStore.DeleteLogo(r.Context(), id); err != nil {
		writeError(w, http.StatusInternalServerError, "failed to delete logo")
		return
	}
	sess := sessionFromCtx(r.Context())
	s.audlog.Log(audit.Entry{
		EventType: "tenant_logo_deleted",
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Success:   true,
		Detail:    "Mandant-Logo gelöscht (tenant " + strconv.FormatInt(id, 10) + ")",
	})
	w.WriteHeader(http.StatusNoContent)
}

// ── Logo handlers (domain_admin: own tenant) ─────────────────────────────────

func (s *Server) handleGetOwnTenantLogo(w http.ResponseWriter, r *http.Request) {
	if s.tenantStore == nil {
		writeError(w, http.StatusServiceUnavailable, "tenant store not available")
		return
	}
	sess := sessionFromCtx(r.Context())
	if sess.TenantID == nil {
		writeError(w, http.StatusBadRequest, "no tenant context")
		return
	}
	data, contentType, err := s.tenantStore.GetLogo(r.Context(), *sess.TenantID)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to load logo")
		return
	}
	if data == nil {
		writeError(w, http.StatusNotFound, "no logo set")
		return
	}
	if contentType == "" {
		contentType = "image/png"
	}
	w.Header().Set("Content-Type", contentType)
	w.Header().Set("X-Content-Type-Options", "nosniff")
	w.Header().Set("Cache-Control", "public, max-age=86400")
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(data)
}

func (s *Server) handleUploadOwnTenantLogo(w http.ResponseWriter, r *http.Request) {
	if s.tenantStore == nil {
		writeError(w, http.StatusServiceUnavailable, "tenant store not available")
		return
	}
	sess := sessionFromCtx(r.Context())
	if sess.TenantID == nil {
		writeError(w, http.StatusBadRequest, "no tenant context")
		return
	}
	s.saveTenantLogo(w, r, *sess.TenantID)
}

func (s *Server) handleDeleteOwnTenantLogo(w http.ResponseWriter, r *http.Request) {
	if s.tenantStore == nil {
		writeError(w, http.StatusServiceUnavailable, "tenant store not available")
		return
	}
	sess := sessionFromCtx(r.Context())
	if sess.TenantID == nil {
		writeError(w, http.StatusBadRequest, "no tenant context")
		return
	}
	if err := s.tenantStore.DeleteLogo(r.Context(), *sess.TenantID); err != nil {
		writeError(w, http.StatusInternalServerError, "failed to delete logo")
		return
	}
	s.audlog.Log(audit.Entry{
		EventType: "tenant_logo_deleted",
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Success:   true,
		Detail:    "Mandant-Logo gelöscht",
	})
	w.WriteHeader(http.StatusNoContent)
}

// saveTenantLogo is the shared multipart upload logic for logo handlers.
func (s *Server) saveTenantLogo(w http.ResponseWriter, r *http.Request, tenantID int64) {
	if err := r.ParseMultipartForm(maxLogoSize); err != nil {
		writeError(w, http.StatusBadRequest, "failed to parse multipart form")
		return
	}
	file, header, err := r.FormFile("logo")
	if err != nil {
		writeError(w, http.StatusBadRequest, "logo file required")
		return
	}
	defer file.Close()

	contentType := header.Header.Get("Content-Type")
	if contentType == "" {
		contentType = "image/png"
	}
	// SVG intentionally NOT allowed (PROJ-61): SVG can carry embedded
	// JavaScript and would be served same-origin as image/svg+xml → stored XSS.
	allowed := map[string]bool{
		"image/png":  true,
		"image/jpeg": true,
		"image/jpg":  true,
		"image/gif":  true,
		"image/webp": true,
	}
	if !allowed[contentType] {
		writeError(w, http.StatusBadRequest, "unsupported image type (allowed: png, jpeg, gif, webp)")
		return
	}

	data, err := io.ReadAll(io.LimitReader(file, maxLogoSize+1))
	if err != nil {
		writeError(w, http.StatusInternalServerError, "failed to read logo")
		return
	}
	if int64(len(data)) > maxLogoSize {
		writeError(w, http.StatusBadRequest, "logo too large (max 2 MB)")
		return
	}

	if err := s.tenantStore.SetLogo(r.Context(), tenantID, data, contentType); err != nil {
		writeError(w, http.StatusInternalServerError, "failed to save logo")
		return
	}

	sess := sessionFromCtx(r.Context())
	s.audlog.Log(audit.Entry{
		EventType: "tenant_logo_uploaded",
		Username:  sess.Username,
		TenantID:  sess.TenantID,
		IPAddress: s.remoteIP(r),
		Success:   true,
		Detail:    fmt.Sprintf("Mandant-Logo hochgeladen (%d bytes, %s, tenant %d)", len(data), contentType, tenantID),
	})

	writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
}
