package onboarding

import (
	"context"
	"fmt"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/attachment"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

type Dependencies struct {
	SessionAuth               api.SessionAuthenticator
	WorkspaceStore            api.WorkspaceResolver
	Store                     Store
	MockExchangeStore         MockExchangeStateStore
	Agents                    AgentStore
	Runtime                   Runtime
	MockMessages              MockMessageBackend
	MockAttachments           MockAttachmentBackend
	MockFanout                MockMessageFanout
	BillingEnabled            bool
	EmailVerificationRequired bool
}

func RegisterRoutes(router gin.IRouter, dependencies Dependencies) {
	if router == nil {
		return
	}
	handler := &Handler{
		store:   dependencies.Store,
		agents:  dependencies.Agents,
		runtime: dependencies.Runtime,
		enabled: dependencies.BillingEnabled,
	}
	mockExchange := &MockExchangeHandler{
		state:       dependencies.MockExchangeStore,
		messages:    mockMessageAdapter{next: dependencies.MockMessages},
		attachments: mockAttachmentAdapter{next: dependencies.MockAttachments},
		fanout:      dependencies.MockFanout,
		enabled:     dependencies.BillingEnabled,
	}
	workspaceScoped := router.Group(
		"/api/v1/workspaces/:workspaceId/onboarding",
		api.RequireAuthenticatedUser(dependencies.SessionAuth),
		api.RequireVerifiedIdentity(dependencies.EmailVerificationRequired),
		api.RequireRouteWorkspace(dependencies.WorkspaceStore),
	)
	workspaceScoped.GET("", api.HandleWorkspaceScoped(handler.Get))
	workspaceScoped.POST("/bootstrap", api.HandleWorkspaceScoped(handler.Bootstrap))
	workspaceScoped.POST("/visibility", api.HandleWorkspaceScopedJSON[dto.DesktopOnboardingVisibilityRequest](handler.Visibility))
	workspaceScoped.POST("/mock-exchange/start", api.HandleWorkspaceScopedJSON[dto.DesktopOnboardingMockStartRequest](mockExchange.MockStart))
	workspaceScoped.POST("/mock-exchange/reply", api.HandleWorkspaceScopedJSON[dto.DesktopOnboardingMockReplyRequest](mockExchange.MockReply))
}

type mockMessageAdapter struct {
	next MockMessageBackend
}

func (adapter mockMessageAdapter) SendDesktopOnboardingMockMessageForUser(ctx context.Context, user identity.User, workspaceID string, targetScopeID string, content string, mention *MockMention, clientMessageID string, startAgentThread bool) (message.AppMessage, message.MessageDelivery, error) {
	if adapter.next == nil {
		return message.AppMessage{}, message.MessageDelivery{}, fmt.Errorf("onboarding mock message backend is not configured")
	}
	var mentionHints []message.MessageMentionHint
	if mention != nil {
		mentionHints = []message.MessageMentionHint{{Type: "agent", ID: mention.AgentID, Token: mention.Token}}
	}
	return adapter.next.SendMessageForUserWithOptions(ctx, user, workspaceID, targetScopeID, content, nil, message.HumanMessageCreateOptions{
		MentionHints: mentionHints, ClientMessageID: clientMessageID,
		StartAgentThread: startAgentThread, SuppressAgentDelivery: true,
	})
}

func (adapter mockMessageAdapter) SendDesktopOnboardingMockAgentReply(ctx context.Context, author MockAgentIdentity, targetRef string, content string, attachmentIDs []string, clientMessageID string) (message.MessageDelivery, error) {
	if adapter.next == nil {
		return message.MessageDelivery{}, fmt.Errorf("onboarding mock message backend is not configured")
	}
	return adapter.next.SendAgentThreadMessage(ctx, agent.AuthenticatedAgent{
		AgentID: author.AgentID, WorkspaceID: author.WorkspaceID, MemberID: author.MemberID, MachineID: author.MachineID,
	}, message.SendMessageParams{
		TargetRef: targetRef, Content: content, AttachmentIDs: attachmentIDs, ClientMessageID: clientMessageID,
	})
}

type mockAttachmentAdapter struct {
	next MockAttachmentBackend
}

func (adapter mockAttachmentAdapter) UploadDesktopOnboardingMockAttachment(ctx context.Context, memberID string, workspaceID string, scopeID string, input MockAttachment) (string, error) {
	if adapter.next == nil {
		return "", fmt.Errorf("onboarding mock attachment backend is not configured")
	}
	created, err := adapter.next.UploadAttachmentsForMember(ctx, memberID, workspaceID, scopeID, []attachment.CreateAttachmentParams{{
		Filename: input.Filename, ContentType: input.ContentType, Blob: input.Blob,
	}})
	if err != nil {
		return "", err
	}
	if len(created) != 1 {
		return "", fmt.Errorf("create onboarding mock attachment: expected one attachment, got %d", len(created))
	}
	return created[0].ID, nil
}
