package httpserver

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

	"github.com/go-chi/chi/v5"
	"github.com/tim/cairnquire/apps/server/internal/collaboration"
)

// Comments

func (s *Server) handleListComments(w http.ResponseWriter, r *http.Request) {
	documentID := r.URL.Query().Get("document_id")
	if documentID == "" {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": "document_id is required"})
		return
	}
	document, err := s.repository.GetDocumentByID(r.Context(), documentID)
	if err != nil {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}
	canRead, err := s.canReadDocumentRecord(r, *document)
	if err != nil {
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	if !canRead {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}

	anchorHash := r.URL.Query().Get("anchor_hash")
	includeResolved := r.URL.Query().Get("include_resolved") == "true"
	var comments []collaboration.Comment
	if anchorHash != "" {
		comments, err = s.collaboration.ListCommentsByAnchor(r.Context(), documentID, anchorHash, includeResolved)
	} else {
		comments, err = s.collaboration.ListComments(r.Context(), documentID, includeResolved)
	}
	if err != nil {
		s.logger.Warn("list comments", "error", err)
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"comments": comments})
}

func (s *Server) handleCreateComment(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	var input collaboration.CreateCommentInput
	if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		return
	}
	document, err := s.repository.GetDocumentByID(r.Context(), input.DocumentID)
	if err != nil {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}
	canWrite, err := s.canWriteDocumentRecord(r, *document)
	if err != nil {
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	if !canWrite {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}

	comment, err := s.collaboration.CreateComment(r.Context(), principal, input)
	if err != nil {
		s.logger.Warn("create comment", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSONWithStatus(w, http.StatusCreated, comment)
}

func (s *Server) handleResolveComment(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	commentID := chi.URLParam(r, "id")
	comment, err := s.collaboration.GetComment(r.Context(), commentID)
	if err != nil {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "comment not found"})
		return
	}
	document, err := s.repository.GetDocumentByID(r.Context(), comment.DocumentID)
	if err != nil {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}
	canWrite, err := s.canWriteDocumentRecord(r, *document)
	if err != nil {
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	if !canWrite {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "comment not found"})
		return
	}
	if err := s.collaboration.ResolveComment(r.Context(), principal, commentID); err != nil {
		s.logger.Warn("resolve comment", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "resolved"})
}

// Notifications

func (s *Server) handleListNotifications(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	unreadOnly := r.URL.Query().Get("unread") == "true"
	limit := 50
	if rawLimit := r.URL.Query().Get("limit"); rawLimit != "" {
		parsed, err := strconv.Atoi(rawLimit)
		if err != nil || parsed < 1 || parsed > 100 {
			writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": "limit must be between 1 and 100"})
			return
		}
		limit = parsed
	}

	notifications, err := s.collaboration.ListNotifications(r.Context(), principal, unreadOnly, limit)
	if err != nil {
		s.logger.Warn("list notifications", "error", err)
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"notifications": notifications})
}

func (s *Server) handleMarkNotificationRead(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	notificationID := chi.URLParam(r, "id")
	if err := s.collaboration.MarkNotificationRead(r.Context(), principal, notificationID); err != nil {
		s.logger.Warn("mark notification read", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
}

func (s *Server) handleMarkAllNotificationsRead(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	if err := s.collaboration.MarkAllNotificationsRead(r.Context(), principal); err != nil {
		s.logger.Warn("mark all notifications read", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
}

func (s *Server) handleNotificationBell(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSON(w, http.StatusOK, collaboration.NotificationBell{UnreadCount: 0, Items: []collaboration.Notification{}})
		return
	}

	bell, err := s.collaboration.GetBellStatus(r.Context(), principal)
	if err != nil {
		s.logger.Warn("notification bell", "error", err)
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, bell)
}

// Watchers

func (s *Server) handleWatchDocument(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	var req struct {
		DocumentID string `json:"documentId"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		return
	}
	req.DocumentID = strings.TrimSpace(req.DocumentID)
	document, err := s.repository.GetDocumentByID(r.Context(), req.DocumentID)
	if err != nil {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}
	canRead, err := s.canReadDocumentRecord(r, *document)
	if err != nil {
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	if !canRead {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}

	if err := s.collaboration.WatchDocument(r.Context(), principal, req.DocumentID); err != nil {
		s.logger.Warn("watch document", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "watching"})
}

func (s *Server) handleUnwatchDocument(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	var req struct {
		DocumentID string `json:"documentId"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		return
	}
	req.DocumentID = strings.TrimSpace(req.DocumentID)

	if err := s.collaboration.UnwatchDocument(r.Context(), principal, req.DocumentID); err != nil {
		s.logger.Warn("unwatch document", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "unwatched"})
}

func (s *Server) handleWatchFolder(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	var req struct {
		FolderPath string `json:"folderPath"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		return
	}
	normalized, err := collaboration.NormalizeFolderPath(req.FolderPath)
	if err != nil {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	if !s.canReadFolder(r, normalized) {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "folder not found"})
		return
	}

	if err := s.collaboration.WatchFolder(r.Context(), principal, normalized); err != nil {
		s.logger.Warn("watch folder", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "watching"})
}

func (s *Server) handleUnwatchFolder(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSONWithStatus(w, http.StatusUnauthorized, map[string]string{"error": "authentication required"})
		return
	}

	var req struct {
		FolderPath string `json:"folderPath"`
	}
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": "invalid request body"})
		return
	}
	normalized, err := collaboration.NormalizeFolderPath(req.FolderPath)
	if err != nil {
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}

	if err := s.collaboration.UnwatchFolder(r.Context(), principal, normalized); err != nil {
		s.logger.Warn("unwatch folder", "error", err)
		writeJSONWithStatus(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]string{"status": "unwatched"})
}

func (s *Server) handleIsWatching(w http.ResponseWriter, r *http.Request) {
	principal, ok := requirePrincipal(r)
	if !ok {
		writeJSON(w, http.StatusOK, map[string]bool{"watching": false})
		return
	}

	documentID := r.URL.Query().Get("document_id")
	document, err := s.repository.GetDocumentByID(r.Context(), documentID)
	if err != nil {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}
	canRead, err := s.canReadDocumentRecord(r, *document)
	if err != nil {
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	if !canRead {
		writeJSONWithStatus(w, http.StatusNotFound, map[string]string{"error": "document not found"})
		return
	}
	watching, err := s.collaboration.IsWatching(r.Context(), principal, documentID)
	if err != nil {
		s.logger.Warn("is watching", "error", err)
		writeJSONWithStatus(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]bool{"watching": watching})
}

// Document page with comments

func (s *Server) renderDocumentPageWithComments(w http.ResponseWriter, r *http.Request, pagePath string) {
	// This is a helper that could be used to inject comments into the template
	// For now, comments are fetched client-side via JS
	s.renderDocumentPage(w, r, pagePath)
}
