package app

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

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

func TestAcceptPendingWorkspaceInviteProjectsInviteeIntoAllChannel(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	pool := startAppRollbackDB(t, ctx)
	store := New(pool, Options{
		CheckWorkspaceQuotaInTx: func(context.Context, coredb.Tx, string, plan.MetricKey, int64) error {
			return nil
		},
	})
	now := time.Now().UTC().Truncate(time.Second)
	admin := seedAppAdminFixture(t, pool, "invite_accept_all_channel_admin", now)
	serviceScopeID := seedAppServiceAccountMessageFixture(t, pool, admin, "invite-late-reader", now)

	inviteeMobile := "+8613800138001"
	invitee := identity.User{
		ID:               "usr_invite_accept_all_channel_invitee",
		Email:            "invite-accept-all-channel@example.com",
		Name:             "invite-accept-all-channel",
		DisplayName:      "Invite Accept All Channel",
		Mobile:           inviteeMobile,
		MobileVerifiedAt: &now,
	}
	mustExec(t, pool, `
INSERT INTO users (id, email, name, display_name, password_hash, is_admin, mobile, mobile_verified_at, created_at, updated_at)
VALUES ($1, $2, $3, $4, 'hash', FALSE, $5, $6, $6, $6)
`, invitee.ID, invitee.Email, invitee.Name, invitee.DisplayName, invitee.Mobile, now)

	inviteID := "inv_invite_accept_all_channel"
	mustExec(t, pool, `
INSERT INTO workspace_invites (id, workspace_id, mobile, role, token_hash, created_by_user_id, created_at, expires_at)
VALUES ($1, $2, $3, 'member', 'hash-invite-accept-all-channel', $4, $5, $6)
`, inviteID, admin.WorkspaceID, inviteeMobile, admin.UserID, now, now.Add(24*time.Hour))

	summary, found, err := store.AcceptWorkspaceInviteByIDIfExists(ctx, invitee, inviteID)
	require.NoError(t, err)
	require.True(t, found)
	require.Equal(t, admin.WorkspaceID, summary.WorkspaceID)
	require.Equal(t, invitee.ID, summary.MemberUserID)
	require.NotEmpty(t, summary.MemberID)
	require.Equal(t, admin.AllChannelID, summary.AllChannelID)

	var organizationMemberID string
	var memberSource string
	require.NoError(t, pool.QueryRow(ctx, `
SELECT id, source
FROM organization_members
WHERE organization_id = $1
  AND user_id = $2
  AND status = 'active'
`, admin.OrganizationID, invitee.ID).Scan(&organizationMemberID, &memberSource))
	require.NotEmpty(t, organizationMemberID)
	require.Equal(t, "workspace_invite", memberSource)

	channels, err := message.ListChannelsForMember(ctx, pool, summary.MemberID, admin.WorkspaceID)
	require.NoError(t, err)
	require.Len(t, channels, 1)
	require.Equal(t, admin.AllChannelID, channels[0].ChannelID)
	require.Equal(t, "all", channels[0].Slug)

	var lastReadSeq, lastReadGlobalSeq, effectiveFromSeq int64
	require.NoError(t, pool.QueryRow(ctx, `
SELECT last_read_seq, last_read_global_seq, effective_from_seq
FROM scope_memberships
WHERE scope_id = $1
  AND member_id = $2
  AND effective_to_seq IS NULL
  AND removed_at IS NULL
  AND archived_at IS NULL
`, serviceScopeID, summary.MemberID).Scan(&lastReadSeq, &lastReadGlobalSeq, &effectiveFromSeq))
	require.Equal(t, int64(1), lastReadSeq)
	require.Equal(t, int64(1), lastReadGlobalSeq)
	require.Equal(t, int64(2), effectiveFromSeq)

	history, err := store.ListMessagesForUser(ctx, invitee.ID, admin.WorkspaceID, serviceScopeID, 20, nil, nil)
	require.NoError(t, err)
	require.Len(t, history.Messages, 1, "a newly invited Human can browse retained service-account history")
}

func TestDismissPendingWorkspaceInviteForUserHidesOnlyThatInvite(t *testing.T) {
	t.Parallel()

	ctx := context.Background()
	pool := startAppRollbackDB(t, ctx)
	store := New(pool, Options{})
	now := time.Now().UTC().Add(-time.Hour).Truncate(time.Second)
	admin := seedAppAdminFixture(t, pool, "invite_dismiss_admin", now)

	inviteeMobile := "+8613800138000"
	invitee := identity.User{
		ID:               "usr_invite_dismiss_invitee",
		Email:            "invite-dismiss-invitee@example.com",
		Name:             "invite-dismiss-invitee",
		DisplayName:      "Invite Dismiss Invitee",
		Mobile:           inviteeMobile,
		MobileVerifiedAt: &now,
	}
	mustExec(t, pool, `
INSERT INTO users (id, email, name, display_name, password_hash, is_admin, mobile, mobile_verified_at, created_at, updated_at)
VALUES ($1, $2, $3, $4, 'hash', FALSE, $5, $6, $6, $6)
`, invitee.ID, invitee.Email, invitee.Name, invitee.DisplayName, invitee.Mobile, now)

	firstInviteID := "inv_invite_dismiss_first"
	mustExec(t, pool, `
INSERT INTO workspace_invites (id, workspace_id, mobile, role, token_hash, created_by_user_id, created_at, expires_at)
VALUES ($1, $2, $3, 'member', 'hash-invite-dismiss-1', $4, $5, $6)
`, firstInviteID, admin.WorkspaceID, inviteeMobile, admin.UserID, now, now.Add(24*time.Hour))

	pending, err := store.ListPendingWorkspaceInvitesForUser(ctx, invitee)
	require.NoError(t, err)
	require.Len(t, pending, 1)
	require.Equal(t, firstInviteID, pending[0].ID)

	found, err := store.DismissPendingWorkspaceInviteForUser(ctx, invitee, firstInviteID)
	require.NoError(t, err)
	require.True(t, found)

	found, err = store.DismissPendingWorkspaceInviteForUser(ctx, invitee, firstInviteID)
	require.NoError(t, err)
	require.True(t, found)

	pending, err = store.ListPendingWorkspaceInvitesForUser(ctx, invitee)
	require.NoError(t, err)
	require.Empty(t, pending)

	secondInviteID := "inv_invite_dismiss_second"
	mustExec(t, pool, `
INSERT INTO workspace_invites (id, workspace_id, mobile, role, token_hash, created_by_user_id, created_at, expires_at)
VALUES ($1, $2, $3, 'member', 'hash-invite-dismiss-2', $4, $5, $6)
`, secondInviteID, admin.WorkspaceID, inviteeMobile, admin.UserID, now.Add(time.Second), now.Add(24*time.Hour))

	pending, err = store.ListPendingWorkspaceInvitesForUser(ctx, invitee)
	require.NoError(t, err)
	require.Len(t, pending, 1)
	require.Equal(t, secondInviteID, pending[0].ID)
}
