package app

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

const approvalAppDefinitionIDForTests = "svcdef_agentteam_approval"

type approvalAppNotificationFixture struct {
	Installation  serviceactor.Installation
	ServiceMember serviceactor.Member
}

func configureApprovalAppRecipientForTests(
	t *testing.T,
	ctx context.Context,
	pool coredb.QueryExecer,
	store *Store,
	fixture appAdminFixture,
	approvalApp approvalAppNotificationFixture,
	businessNamespace string,
	businessType string,
	basisKind string,
	basisRef string,
) {
	t.Helper()

	digest := strings.Repeat("a", 64)
	policy, err := json.Marshal(map[string]any{
		"recipient_policy_version":           1,
		"allowed_recipient_basis_kinds":      []string{basisKind},
		"allowed_business_object_namespaces": []string{businessNamespace},
		"allowed_business_object_types":      []string{businessType},
		"allowed_human_dm_content_kinds":     []string{AppNotificationContentInteractiveCard, AppNotificationContentMessage},
		"organization_policy_ref":            "agentteam.approval.test",
		"policy_digest":                      digest,
	})
	require.NoError(t, err)
	_, err = pool.Exec(ctx, `
UPDATE service_grants
SET config = $1,
    policy_version = 1,
    policy_digest = $2,
    updated_at = CURRENT_TIMESTAMP
WHERE service_installation_id = $3
  AND scope = $4
  AND revoked_at IS NULL
`, policy, digest, approvalApp.Installation.ID, AppNotificationScopeWrite)
	require.NoError(t, err)
	require.NoError(t, store.RegisterAppNotificationRecipientResolver(basisKind, &recordingAppNotificationRecipientResolver{
		resolveFact: AppNotificationRecipientFact{
			Kind: basisKind, Ref: basisRef, WorkspaceID: fixture.WorkspaceID,
			RecipientMemberID: fixture.MemberID, Version: 1, Status: "active", Active: true,
		},
	}))
}

func installApprovalAppForNotificationTests(t *testing.T, ctx context.Context, pool coredb.DB, fixture appAdminFixture, suffix string, now time.Time) approvalAppNotificationFixture {
	t.Helper()

	suffix = strings.ReplaceAll(strings.TrimSpace(suffix), "-", "_")
	memberID := fmt.Sprintf("mem_approval_bot_%s", suffix)
	installationID := fmt.Sprintf("appinst_approval_bot_%s", suffix)
	serviceMemberID := fmt.Sprintf("svcmem_approval_bot_%s", suffix)
	metadata := []byte(`{
		"builtin":true,
		"identity":"agentteam.approval",
		"version":"1.0.0",
		"manifest":{
			"schema_version":"1",
			"slug":"agentteam-approval",
			"name":"Approval",
			"version":"1.0.0",
			"capabilities":{"runtime":{
				"runtime":{"transports":["internal_handler"],"default_transport":"internal_handler"},
				"events":["agentteam.app.notification.action"],
				"scopes":["messages.write","app.notifications.write","app.notification_actions.read"],
				"notifications":{"targets":["human_dm"],"content":["message","interactive_card"]}
			}}
		}
	}`)

	var result approvalAppNotificationFixture
	require.NoError(t, coredb.WithTx(ctx, pool, func(tx coredb.Tx) error {
		actors := serviceactor.New(pool).InTx(tx)
		if err := actors.EnsureServiceMemberSubstrate(ctx, serviceactor.ServiceMemberSubstrateInsert{
			ID: memberID, WorkspaceID: fixture.WorkspaceID, DisplayName: "Approval", Now: now,
		}); err != nil {
			return err
		}
		installation, err := actors.UpsertInstallation(ctx, serviceactor.InstallationUpsert{
			ID: installationID, WorkspaceID: fixture.WorkspaceID,
			ServiceDefinitionID: approvalAppDefinitionIDForTests, ServiceKind: serviceactor.KindApp,
			InstanceKey: serviceactor.DefaultInstanceKey, Status: serviceactor.StatusActive,
			InstalledVersion: "1.0.0", Config: []byte(`{}`), Metadata: metadata,
			CreatedByMemberID: fixture.MemberID, CreatedByActorType: "internal_service",
			CreatedByActorID: "agentteam.approval", Now: now,
		})
		if err != nil {
			return err
		}
		member, err := actors.UpsertMember(ctx, serviceactor.MemberUpsert{
			ID: serviceMemberID, WorkspaceID: fixture.WorkspaceID,
			ServiceDefinitionID:   approvalAppDefinitionIDForTests,
			ServiceInstallationID: installation.ID, MemberID: memberID,
			ServiceKind: serviceactor.KindApp, Handle: "agentteam-approval",
			DisplayName: "Approval", Status: serviceactor.StatusActive, Now: now,
		})
		if err != nil {
			return err
		}
		if err := actors.SyncGrantScopes(ctx, installation, fixture.MemberID, []string{
			AppRuntimeScopeMessageWrite, AppNotificationScopeWrite, AppNotificationActionScopeRead,
		}, now); err != nil {
			return err
		}
		result = approvalAppNotificationFixture{Installation: installation, ServiceMember: member}
		return nil
	}))
	return result
}
