package im

import (
	"fmt"
	"net/http"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto/converter"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

// PinsHandler owns shared message pins and the separate top-message banner.
type PinsHandler struct {
	pins         PinStore
	streamWriter StreamEventWriter
}

// List handles GET /api/v1/workspaces/:workspaceId/scopes/:scopeId/pins.
func (h *PinsHandler) List(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, err := routeScopeID(r)
	if err != nil {
		return api.Response{}, err
	}
	limit, err := parseLimit(r.Gin.Query("limit"), message.DefaultMessagePinListLimit, message.MaxMessagePinListLimit)
	if err != nil {
		return api.Response{}, err
	}
	cursor, err := message.ParseMessagePinCursorToken(r.Gin.Query("cursor"))
	if err != nil {
		return api.Response{}, err
	}
	list, err := h.pins.ListMessagePinsForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID, cursor, limit)
	if err != nil {
		return api.Response{}, err
	}
	if err := requireMessagePinListScope(scopeID, list); err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToMessagePinList(list)), nil
}

// Pin handles PUT /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/pin.
func (h *PinsHandler) Pin(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, messageID, err := routeScopeMessage(r)
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.pins.PinMessageForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID, messageID)
	if err != nil {
		return api.Response{}, err
	}
	if err := requireMessagePinListScope(scopeID, result.List); err != nil {
		return api.Response{}, err
	}
	if result.Changed {
		if err := publishMessagePinChangedEvent(r.Context(), h.streamWriter, r.WorkspaceID(), scopeID, result); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(converter.ToMessagePinMutationResponse(result)), nil
}

// UnpinForEveryone handles DELETE /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/pin.
func (h *PinsHandler) UnpinForEveryone(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, messageID, err := routeScopeMessage(r)
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.pins.UnpinMessageForEveryoneForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID, messageID)
	if err != nil {
		return api.Response{}, err
	}
	if err := requireMessagePinListScope(scopeID, result.List); err != nil {
		return api.Response{}, err
	}
	if result.Changed {
		if err := publishMessagePinChangedEvent(r.Context(), h.streamWriter, r.WorkspaceID(), scopeID, result); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(converter.ToMessagePinMutationResponse(result)), nil
}

// GetTop handles GET /api/v1/workspaces/:workspaceId/scopes/:scopeId/top-notice.
func (h *PinsHandler) GetTop(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, err := routeScopeID(r)
	if err != nil {
		return api.Response{}, err
	}
	state, err := h.pins.GetMessageTopNoticeForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToMessageTopNoticeState(state)), nil
}

// Top handles PUT /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/top-notice.
func (h *PinsHandler) Top(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, messageID, err := routeScopeMessage(r)
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.pins.TopMessageForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID, messageID)
	if err != nil {
		return api.Response{}, err
	}
	if result.Changed {
		if err := publishMessageTopNoticeChangedEvent(r.Context(), h.streamWriter, r.WorkspaceID(), scopeID, result); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(converter.ToMessageTopNoticeMutationResponse(result)), nil
}

// UntopForEveryone handles DELETE /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/top-notice.
func (h *PinsHandler) UntopForEveryone(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, messageID, err := routeScopeMessage(r)
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.pins.UntopMessageForEveryoneForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID, messageID)
	if err != nil {
		return api.Response{}, err
	}
	if result.Changed {
		if err := publishMessageTopNoticeChangedEvent(r.Context(), h.streamWriter, r.WorkspaceID(), scopeID, result); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(converter.ToMessageTopNoticeMutationResponse(result)), nil
}

// HideTopBannerForMe handles PUT /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/top-notice/banner-hide.
func (h *PinsHandler) HideTopBannerForMe(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, messageID, err := routeScopeMessage(r)
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.pins.HideMessageTopNoticeBannerForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID, messageID, strings.TrimSpace(r.Gin.Query("topNoticeId")))
	if err != nil {
		return api.Response{}, err
	}
	if result.Changed {
		if err := publishMessageTopNoticeBannerStateChangedEvent(r.Context(), h.streamWriter, r.WorkspaceID(), result.State.ViewerMemberID, scopeID, result); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(converter.ToMessageTopNoticeMutationResponse(result)), nil
}

// ShowTopBannerForMe handles DELETE /api/v1/workspaces/:workspaceId/scopes/:scopeId/messages/:messageId/top-notice/banner-hide.
func (h *PinsHandler) ShowTopBannerForMe(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, messageID, err := routeScopeMessage(r)
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.pins.ShowMessageTopNoticeBannerForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID, messageID, strings.TrimSpace(r.Gin.Query("topNoticeId")))
	if err != nil {
		return api.Response{}, err
	}
	if result.Changed {
		if err := publishMessageTopNoticeBannerStateChangedEvent(r.Context(), h.streamWriter, r.WorkspaceID(), result.State.ViewerMemberID, scopeID, result); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(converter.ToMessageTopNoticeMutationResponse(result)), nil
}

// ResetTopBannerHidesForMe handles DELETE /api/v1/workspaces/:workspaceId/scopes/:scopeId/top-notice-hides.
func (h *PinsHandler) ResetTopBannerHidesForMe(r api.WorkspaceScopedRequest) (api.Response, error) {
	if h.pins == nil {
		return api.Response{}, api.NewHTTPError(http.StatusInternalServerError, "Internal server error")
	}
	scopeID, err := routeScopeID(r)
	if err != nil {
		return api.Response{}, err
	}
	result, err := h.pins.ResetMessageTopNoticeHidesForUser(r.Context(), r.UserID(), r.WorkspaceID(), scopeID)
	if err != nil {
		return api.Response{}, err
	}
	if result.Changed {
		if err := publishMessageTopNoticeBannerStateChangedEvent(r.Context(), h.streamWriter, r.WorkspaceID(), result.State.ViewerMemberID, scopeID, result); err != nil {
			return api.Response{}, err
		}
	}
	return api.OK(converter.ToMessageTopNoticeMutationResponse(result)), nil
}

func requireMessagePinListScope(routeScopeID string, list message.MessagePinList) error {
	normalizedRouteScopeID := strings.TrimSpace(routeScopeID)
	if strings.TrimSpace(list.ScopeID) != normalizedRouteScopeID {
		return api.NewHTTPErrorWithCause(
			http.StatusInternalServerError,
			"Internal server error",
			fmt.Errorf("message pin list scope %q does not match route scope %q", list.ScopeID, routeScopeID),
		)
	}
	for _, pin := range list.Pins {
		if strings.TrimSpace(pin.ScopeID) != normalizedRouteScopeID || strings.TrimSpace(pin.Message.ChannelID) != normalizedRouteScopeID {
			return api.NewHTTPErrorWithCause(
				http.StatusInternalServerError,
				"Internal server error",
				fmt.Errorf("message pin %q does not belong to route scope %q", pin.ID, routeScopeID),
			)
		}
	}
	return nil
}

func routeScopeMessage(r api.WorkspaceScopedRequest) (string, string, error) {
	scopeID, err := routeScopeID(r)
	if err != nil {
		return "", "", err
	}
	messageID, err := routeMessageID(r)
	if err != nil {
		return "", "", err
	}
	return scopeID, messageID, nil
}
