package server

import (
	"bytes"
	"encoding/json"
	"io"
	"mime"
	"net/http"
	"net/url"
	"strings"
	"time"

	"prism/service/delivery"
	"prism/service/util"

	"github.com/go-chi/chi/v5"
)

func (s *Server) handleNtfyPublish(w http.ResponseWriter, r *http.Request) {
	appName := chi.URLParam(r, "appName")
	decodedAppName, err := url.PathUnescape(appName)
	if err != nil {
		util.JSONError(w, "Invalid app name", http.StatusBadRequest)
		return
	}
	appName = decodedAppName
	if appName == "" || strings.Contains(appName, "/") {
		util.JSONError(w, "Invalid app name", http.StatusBadRequest)
		return
	}

	body, err := io.ReadAll(r.Body)
	if err != nil {
		util.JSONError(w, "Failed to read body", http.StatusBadRequest)
		return
	}

	message, title := parseNtfyPayload(r, body)

	if message == "" {
		util.JSONError(w, "Message required", http.StatusBadRequest)
		return
	}

	if title == appName {
		title = ""
	}

	notif := delivery.Notification{
		Title:   title,
		Message: message,
	}

	if err := s.publisher.Publish(appName, notif); err != nil {
		util.LogAndError(w, s.logger, "Failed to send notification", http.StatusInternalServerError, err, "app", appName)
		return
	}

	s.logger.Debug("Sent ntfy message", "app", appName, "title", title, "preview", truncate(message, 50))

	now := time.Now()
	w.Header().Set("Content-Type", "application/json")
	response := map[string]any{
		"id":      now.UnixNano(),
		"time":    now.Unix(),
		"event":   "message",
		"topic":   appName,
		"message": message,
	}
	if err := json.NewEncoder(w).Encode(response); err != nil {
		s.logger.Error("Failed to encode response", "error", err)
	}
}

func parseNtfyPayload(r *http.Request, body []byte) (string, string) {
	var message, title string

	mediaType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type"))
	if err != nil {
		mediaType = ""
	}

	switch mediaType {
	case "application/json":
		var payload struct {
			Title   string `json:"title"`
			Message string `json:"message"`
		}
		if err := json.Unmarshal(body, &payload); err == nil {
			message = payload.Message
			title = payload.Title
		} else {
			message = string(body)
		}
	case "application/x-www-form-urlencoded":
		r.Body = io.NopCloser(bytes.NewReader(body))
		if err := r.ParseForm(); err == nil {
			message = r.PostForm.Get("message")
			if message == "" {
				message = string(body)
			}
			title = firstNonEmpty(r.PostForm.Get("title"), r.PostForm.Get("t"))
		} else {
			message = string(body)
		}
	default:
		message = string(body)
	}

	if title == "" {
		title = firstNonEmpty(r.Header.Get("X-Title"), r.Header.Get("Title"), r.Header.Get("t"))
	}

	return message, title
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		if value != "" {
			return value
		}
	}
	return ""
}

func truncate(s string, max int) string {
	if len(s) <= max {
		return s
	}
	return s[:max]
}
