package app

import (
	"context"
	"errors"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

func mapMessagingError(err error) error {
	if err == nil {
		return nil
	}
	switch {
	case errors.Is(err, domain.ErrInvalidArgument):
		return badRequest(err.Error())
	case errors.Is(err, message.ErrConflict):
		return conflict(err.Error())
	case errors.Is(err, message.ErrForbidden):
		return ErrForbidden
	case errors.Is(err, message.ErrNotFound):
		return ErrNotFound
	default:
		return err
	}
}

func messagingActor(authenticatedAgent agent.AuthenticatedAgent) message.MemberActor {
	return message.MemberActor{
		WorkspaceID: authenticatedAgent.WorkspaceID,
		MemberID:    authenticatedAgent.MemberID,
	}
}

func agentTargetAccessMiss(err error) bool {
	return errors.Is(err, message.ErrForbidden) ||
		errors.Is(err, message.ErrNotFound) ||
		errors.Is(err, ErrForbidden) ||
		errors.Is(err, ErrNotFound)
}

func (store *Store) resolveSendTarget(ctx context.Context, tx coredb.Tx, authorAgent agent.AuthenticatedAgent, targetRef string, dmTo string) (message.ThreadAccess, error) {
	if dmTo != "" {
		target, err := message.ResolveSendTarget(ctx, tx, messagingActor(authorAgent), targetRef, dmTo)
		if err != nil {
			return message.ThreadAccess{}, mapMessagingError(err)
		}
		return target, nil
	}

	request, parseErr := parseAgentScopeTargetRequest(targetRef)
	var resolveErr error
	if parseErr == nil {
		target, _, err := store.resolveAgentThreadTarget(ctx, tx, authorAgent, request, agentTargetResolveOptions{
			Role:              agentTargetResolutionRoleWrite,
			CreateReplyThread: true,
		})
		if err == nil {
			return target, nil
		}
		resolveErr = err
		if !agentTargetAccessMiss(err) {
			return message.ThreadAccess{}, err
		}
	}

	if parseErr != nil {
		return message.ThreadAccess{}, parseErr
	}
	if strings.TrimSpace(request.Reference.ShortID) == "" || store.archiveStorage == nil || !errors.Is(resolveErr, ErrNotFound) {
		if resolveErr != nil {
			return message.ThreadAccess{}, resolveErr
		}
		return message.ThreadAccess{}, ErrNotFound
	}

	summary, access, archiveErr := store.messageStore().InTx(tx).EnsureArchivedReplyThreadForAgentTarget(ctx, messagingActor(authorAgent), request.Reference)
	if archiveErr != nil {
		return message.ThreadAccess{}, mapMessagingError(archiveErr)
	}

	return message.ThreadAccess{
		ThreadID:     access.ThreadID,
		ThreadType:   access.ParentThreadType,
		ChannelID:    summary.ThreadChannelID,
		PeerMemberID: access.PeerMemberID,
		ReplyThread: &message.ReplyThreadTarget{
			ParentMessageID:   access.ParentMessageID,
			ShortID:           access.ParentMessageShort,
			ParentChannelID:   access.ParentChannelID,
			ParentChannelName: access.ParentChannelName,
		},
	}, nil
}

func resolveAccessibleThread(ctx context.Context, queryer coredb.Queryer, authenticatedAgent agent.AuthenticatedAgent, reference domain.ThreadReference) (message.ThreadAccess, error) {
	target, err := message.ResolveAccessibleThread(ctx, queryer, messagingActor(authenticatedAgent), reference)
	if err != nil {
		return message.ThreadAccess{}, mapMessagingError(err)
	}
	return target, nil
}

func (store *Store) threadIDForChannelAccess(ctx context.Context, memberID string, workspaceID string, channelID string) (string, error) {
	threadID, err := message.ThreadIDForChannelAccess(ctx, store.db, memberID, workspaceID, channelID)
	if err != nil {
		return "", mapMessagingError(err)
	}
	return threadID, nil
}
