package channel

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

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

// ChannelCRUDStore is the channel collaboration surface used by ChannelCRUDHandler.
type ChannelCRUDStore interface {
	ListChannelsForUser(ctx context.Context, userID string, workspaceID string) ([]message.ChannelSummary, error)
	CreateChannelForUser(ctx context.Context, userID string, workspaceID string, name string) (message.ChannelSummary, error)
}

// ChannelStore is the route-level channel store dependency.
type ChannelStore interface {
	ChannelCRUDStore
	ChannelDiscoveryStore
	ChannelAccessStore
	ChannelMembershipStore
}

// ChannelCRUDAppStore is the app-channel surface used by ChannelCRUDHandler.
type ChannelCRUDAppStore interface {
	UpdateChannelForUser(ctx context.Context, userID string, workspaceID string, channelID string, name *string, description *string, visibility *string) (message.ChannelSummary, error)
	DeleteChannelOrDMForUser(ctx context.Context, userID string, workspaceID string, channelID string) error
}

// ChannelShareCardAppStore owns channel share-card mutations.
type ChannelShareCardAppStore interface {
	CreateChannelShareCardForUser(ctx context.Context, user model.User, workspaceID string, sourceChannelID string, targetChannelID string) (message.AppMessage, message.MessageDelivery, error)
	ShareChannelShareCardForUser(ctx context.Context, user model.User, workspaceID string, messageID string, targetChannelID string) (message.AppMessage, message.MessageDelivery, error)
	LoadChannelShareCardForUser(ctx context.Context, userID string, workspaceID string, messageID string) (message.ChannelShareCardView, error)
	JoinChannelShareCardForUser(ctx context.Context, userID string, workspaceID string, messageID string) (message.ChannelSummary, error)
	RevokeChannelShareCardForUser(ctx context.Context, userID string, workspaceID string, messageID string) (message.ChannelShareCardView, error)
}

type MessageFanout interface {
	EmitCommittedHumanMessage(ctx context.Context, workspaceID string, message message.AppMessage, delivery message.MessageDelivery)
	EmitCommittedMessageProjectionOnly(ctx context.Context, workspaceID string, message message.AppMessage, delivery message.MessageDelivery)
	EmitMessageUpdated(ctx context.Context, workspaceID string, message message.AppMessage)
}

// ChannelAppStore is the route-level app-channel store dependency.
type ChannelAppStore interface {
	ChannelAttachmentStore
	ChannelCRUDAppStore
	ChannelShareCardAppStore
	ThreadAppStore
	ChannelLifecycleAppStore
	ChannelMembershipAppStore
	ChannelDMStore
}

// ChannelRealtime is the realtime port used by channel mutation routes.
type ChannelRealtime interface {
	EmitChannelUpdated(workspaceID string)
	EmitChannelUpdatedForUser(workspaceID string, userID string)
	EmitChannelMembersUpdated(workspaceID string, channelID string)
	ClearAgentWakeStatusesForChannel(workspaceID string, channelID string, agentID string)
	EmitUnreadUpdated(workspaceID string, delta unread.UnreadDelta)
	// SubscribeScopeForUser adds the scope to every active streaming
	// connection owned by the user, mirroring the old Socket.IO
	// SocketsJoin-by-user-room used when a user is added to a channel.
	SubscribeScopeForUser(workspaceID string, userID string, scopeID string)
	// UnsubscribeScopeForUser removes the scope from every active streaming
	// connection owned by the user, mirroring the old Socket.IO
	// SocketsLeave-by-user-room used when a user leaves or is removed.
	UnsubscribeScopeForUser(workspaceID string, userID string, scopeID string)
}

// ChannelCRUDHandler owns browser-facing channel CRUD routes.
type ChannelCRUDHandler struct {
	channels      ChannelCRUDStore
	appStore      ChannelCRUDAppStore
	realtime      ChannelRealtime
	unreadRuntime ChannelUnreadReader
}

// List handles GET /api/channels.
// Returns all channels the authenticated user belongs to.
func (h *ChannelCRUDHandler) List(r api.WorkspaceScopedRequest) (api.Response, error) {
	authenticated := r.User
	workspaceID := r.WorkspaceID()

	summaries, err := h.channels.ListChannelsForUser(r.Context(), authenticated.User.ID, workspaceID)

	if err != nil {
		return api.Response{}, err
	}

	channels := make([]dto.ChannelResponse, 0, len(summaries))
	for _, summary := range summaries {
		channels = append(channels, channelResponseFromSummary(summary))
	}

	return api.OK(channels), nil
}

// Create handles POST /api/channels.
// Creates a new private channel with the given name.
func (h *ChannelCRUDHandler) Create(r api.WorkspaceScopedJSON[dto.ChannelCreateRequest]) (api.Response, error) {
	request := r.Body
	authenticated := r.User
	workspaceID := r.WorkspaceID()

	channel, err := h.channels.CreateChannelForUser(
		r.Context(),

		authenticated.User.ID,
		workspaceID,
		strings.TrimSpace(request.Name),
	)
	if err != nil {
		return api.Response{}, err
	}
	if h.realtime != nil {
		h.realtime.EmitChannelUpdated(workspaceID)
	}

	return api.OK(channelResponseFromSummary(channel)), nil
}

// Update handles PATCH /api/channels/:channelId.
// Updates the name, description, or visibility of an existing channel.
func (h *ChannelCRUDHandler) Update(r api.WorkspaceScopedJSON[dto.ChannelUpdateRequest]) (api.Response, error) {
	request := r.Body
	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")
	}

	channel, err := h.appStore.UpdateChannelForUser(r.Context(), authenticated.User.ID, workspaceID, channelID, request.Name, request.Description, request.Visibility)

	if err != nil {
		return api.Response{}, err
	}
	if h.realtime != nil {
		h.realtime.EmitChannelUpdated(workspaceID)
	}

	return api.OK(channelResponseFromSummary(channel)), nil
}

// Delete handles DELETE /api/channels/:channelId.
// Deletes a channel or DM for the authenticated user.
func (h *ChannelCRUDHandler) Delete(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")
	}

	if err := h.appStore.DeleteChannelOrDMForUser(r.Context(), authenticated.User.ID, workspaceID, channelID); err != nil {
		return api.Response{}, err

	}
	applyChannelMemberScopeInvalidated(h.realtime, h.unreadRuntime, workspaceID, authenticated.User.ID, r.Workspace.MemberID, channelID, unread.ScopeTypeChannel, "channel_delete")
	if h.realtime != nil {
		h.realtime.EmitChannelUpdated(workspaceID)
	}
	return api.OK(dto.EmptyResponse{}), nil
}
