package reaction

import (
	"context"
	"fmt"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/access"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

var (
	channelReadAction = access.HumanAction{
		Resource: access.ResourceChannel,
		Action:   access.ActionRead,
	}
	dmReadAction = access.HumanAction{
		Resource: access.ResourceDM,
		Action:   access.ActionRead,
	}
	threadReadAction = access.HumanAction{
		Resource: access.ResourceThread,
		Action:   access.ActionRead,
	}
)

// HumanMessageTargetQueryer is the query interface needed to resolve a browser
// user's authorized target inside either the domain DB or a caller transaction.
type HumanMessageTargetQueryer interface {
	QueryRow(context.Context, string, ...any) coredb.Row
	Query(context.Context, string, ...any) (coredb.Rows, error)
}

// MemberIdentityQueryer is the query interface needed to resolve public member
// display names for reaction payloads.
type MemberIdentityQueryer interface {
	Query(context.Context, string, ...any) (coredb.Rows, error)
}

type Options struct {
	LookupMessageChannelID                         func(context.Context, string, string) (string, error)
	AuthorizeMessageSidecarReadForUser             func(context.Context, coredb.QueryExecer, string, string, string, message.SidecarKind) error
	AuthorizeMessageSidecarReadForMember           func(context.Context, coredb.QueryExecer, string, string, string, message.SidecarKind) error
	ResolveAuthorizedHumanMessageTargetWithQueryer func(context.Context, HumanMessageTargetQueryer, string, string, string, access.HumanAction, access.HumanAction, access.HumanAction) (access.HumanWorkspaceMember, message.ThreadAccess, error)
	LoadPublicMemberNamesByMemberID                func(context.Context, MemberIdentityQueryer, string, []string) (map[string]string, error)
}

// Store owns reaction persistence and query logic.
type Store struct {
	db      coredb.DB
	options Options
}

func New(db coredb.DB, options Options) *Store {
	return &Store{db: db, options: options}
}

func (s *Store) Validate() error { return s.options.Validate() }

func (o Options) Validate() error {
	var missing []string
	if o.LookupMessageChannelID == nil {
		missing = append(missing, "LookupMessageChannelID")
	}
	if o.AuthorizeMessageSidecarReadForUser == nil {
		missing = append(missing, "AuthorizeMessageSidecarReadForUser")
	}
	if o.AuthorizeMessageSidecarReadForMember == nil {
		missing = append(missing, "AuthorizeMessageSidecarReadForMember")
	}
	if o.ResolveAuthorizedHumanMessageTargetWithQueryer == nil {
		missing = append(missing, "ResolveAuthorizedHumanMessageTargetWithQueryer")
	}
	if o.LoadPublicMemberNamesByMemberID == nil {
		missing = append(missing, "LoadPublicMemberNamesByMemberID")
	}
	if len(missing) > 0 {
		return fmt.Errorf("reaction store: missing dependencies: %s", strings.Join(missing, ", "))
	}
	return nil
}

func (store *Store) authorizeMessageSidecarReadForUser(ctx context.Context, queryer coredb.QueryExecer, userID string, workspaceID string, messageID string, kind message.SidecarKind) error {
	if store == nil || store.options.AuthorizeMessageSidecarReadForUser == nil {
		return fmt.Errorf("reaction store message sidecar user authority dependency is not configured")
	}
	return store.options.AuthorizeMessageSidecarReadForUser(ctx, queryer, userID, workspaceID, messageID, kind)
}

func (store *Store) authorizeMessageSidecarReadForMember(ctx context.Context, queryer coredb.QueryExecer, memberID string, workspaceID string, messageID string, kind message.SidecarKind) error {
	if store == nil || store.options.AuthorizeMessageSidecarReadForMember == nil {
		return fmt.Errorf("reaction store message sidecar member authority dependency is not configured")
	}
	return store.options.AuthorizeMessageSidecarReadForMember(ctx, queryer, memberID, workspaceID, messageID, kind)
}

func (store *Store) lookupMessageChannelID(ctx context.Context, workspaceID string, messageID string) (string, error) {
	if store == nil || store.options.LookupMessageChannelID == nil {
		return "", fmt.Errorf("reaction store message channel dependency is not configured")
	}
	return store.options.LookupMessageChannelID(ctx, workspaceID, messageID)
}

func (store *Store) resolveAuthorizedHumanMessageTargetWithQueryer(ctx context.Context, queryer HumanMessageTargetQueryer, userID string, workspaceID string, channelID string, channelAction access.HumanAction, dmAction access.HumanAction, threadAction access.HumanAction) (access.HumanWorkspaceMember, message.ThreadAccess, error) {
	if store == nil || store.options.ResolveAuthorizedHumanMessageTargetWithQueryer == nil {
		return access.HumanWorkspaceMember{}, message.ThreadAccess{}, fmt.Errorf("reaction store human target dependency is not configured")
	}
	return store.options.ResolveAuthorizedHumanMessageTargetWithQueryer(ctx, queryer, userID, workspaceID, channelID, channelAction, dmAction, threadAction)
}

func (store *Store) loadPublicMemberNamesByMemberID(ctx context.Context, queryer MemberIdentityQueryer, workspaceID string, memberIDs []string) (map[string]string, error) {
	if store == nil || store.options.LoadPublicMemberNamesByMemberID == nil {
		return nil, fmt.Errorf("reaction store public identity dependency is not configured")
	}
	return store.options.LoadPublicMemberNamesByMemberID(ctx, queryer, workspaceID, memberIDs)
}
