package channel

import (
	"context"
	"net/http"
	"strconv"
	"strings"

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

const (
	defaultDMListLimit = 100
	maxDMListLimit     = 100
)

// ChannelDMStore is the narrow app-channel surface used by DM routes.
type ChannelDMStore interface {
	ListDMChannelsForUserWithLimit(ctx context.Context, userID string, workspaceID string, limit int) ([]message.DMChannel, error)
	GetDMChannelForUser(ctx context.Context, userID string, workspaceID string, channelID string) (message.DMChannel, error)
	OpenDMChannelWithAgent(ctx context.Context, userID string, workspaceID string, agentID string) (message.DMChannel, error)
	OpenDMChannelWithUser(ctx context.Context, userID string, workspaceID string, peerUserID string) (message.DMChannel, error)
	HideDMChannelForUser(ctx context.Context, userID string, workspaceID string, channelID string) (message.DMChannel, error)
}

// ChannelDMHandler owns browser-facing direct-message channel routes.
type ChannelDMHandler struct {
	appStore      ChannelDMStore
	realtime      channelUnreadRealtime
	unreadRuntime ChannelUnreadReader
}

// ListDM handles GET /api/channels/dm.
// Returns all DM channels for the authenticated user.
func (h *ChannelDMHandler) ListDM(r api.WorkspaceScopedRequest) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()

	limit, err := parseDMLimit(r.Gin.Query("limit"))
	if err != nil {
		return api.Response{}, err
	}

	dms, err := h.appStore.ListDMChannelsForUserWithLimit(r.Context(), authenticated.User.ID, workspaceID, limit)

	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToDMChannels(dms)), nil
}

// GetDM handles GET /api/channels/dm/:channelId.
// It returns one DM channel in the authenticated user's perspective.
func (h *ChannelDMHandler) GetDM(r api.WorkspaceScopedRequest) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	channelID := strings.TrimSpace(r.Gin.Param("channelId"))
	if channelID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "channelId is required")
	}

	dm, err := h.appStore.GetDMChannelForUser(r.Context(), authenticated.User.ID, workspaceID, channelID)
	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToDMChannel(dm)), nil
}

func parseDMLimit(raw string) (int, error) {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return defaultDMListLimit, nil
	}
	limit, err := strconv.Atoi(trimmed)
	if err != nil || limit <= 0 {
		return 0, api.NewHTTPError(http.StatusBadRequest, "limit must be a positive integer")
	}
	if limit > maxDMListLimit {
		return maxDMListLimit, nil
	}
	return limit, nil
}

// OpenDM handles POST /api/channels/dm.
// Opens or creates a DM channel with an agent or user.
func (h *ChannelDMHandler) OpenDM(r api.WorkspaceScopedJSON[dto.OpenDMRequest]) (api.Response, error) {
	request := r.Body
	authenticated := r.User
	workspaceID := r.WorkspaceID()

	agentID := strings.TrimSpace(request.AgentID)
	userID := strings.TrimSpace(request.UserID)
	if agentID == "" && userID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "agentId or userId is required")
	}
	if agentID != "" && userID != "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "agentId and userId are mutually exclusive")
	}

	if agentID != "" {

		dm, err := h.appStore.OpenDMChannelWithAgent(r.Context(), authenticated.User.ID, workspaceID, agentID)

		if err != nil {
			return api.Response{}, err
		}
		return api.OK(converter.ToDMChannel(dm)), nil
	}

	dm, err := h.appStore.OpenDMChannelWithUser(r.Context(), authenticated.User.ID, workspaceID, userID)

	if err != nil {
		return api.Response{}, err
	}
	return api.OK(converter.ToDMChannel(dm)), nil
}

// HideDM handles POST /api/channels/dm/:channelId/hide.
// Hides one DM from the caller's recent-DM sidebar view until a newer message arrives.
func (h *ChannelDMHandler) HideDM(r api.WorkspaceScopedRequest) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()
	channelID := strings.TrimSpace(r.Gin.Param("channelId"))
	if channelID == "" {
		return api.Response{}, api.NewHTTPError(http.StatusBadRequest, "channelId is required")
	}

	dm, err := h.appStore.HideDMChannelForUser(r.Context(), authenticated.User.ID, workspaceID, channelID)
	if err != nil {
		return api.Response{}, err
	}
	applyChannelMemberScopeInvalidated(h.realtime, h.unreadRuntime, workspaceID, authenticated.User.ID, r.Workspace.MemberID, channelID, unread.ScopeTypeDM, "dm_hide")
	return api.OK(converter.ToDMChannel(dm)), nil
}
