package serviceactor

import (
	"context"
	"database/sql"
	"errors"
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

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

func TestAppRuntimeSubscriptionKindAllowlist(t *testing.T) {
	if got, want := AppRuntimeSubscriptionKinds(), []string{SubscriptionKindAppMention, SubscriptionKindAppNotificationAction}; !reflect.DeepEqual(got, want) {
		t.Fatalf("AppRuntimeSubscriptionKinds() = %#v, want %#v", got, want)
	}
	for _, kind := range AppRuntimeSubscriptionKinds() {
		if !IsAppRuntimeSubscriptionKind(" " + kind + " ") {
			t.Fatalf("IsAppRuntimeSubscriptionKind(%q) = false", kind)
		}
	}
	if IsAppRuntimeSubscriptionKind(SubscriptionKindPullRequest) {
		t.Fatal("pull_request unexpectedly accepted as App runtime kind")
	}
}

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

	stopAfterInsert := errors.New("stop after insert")
	execer := &eventSubscriptionInsertCapture{execErr: stopAfterInsert}
	now := time.Date(2026, 6, 9, 14, 30, 0, 0, time.UTC)

	_, _, err := upsertEventSubscription(context.Background(), execer, EventSubscriptionUpsert{
		ID:                     "svcesub_json_text",
		WorkspaceID:            "wsp_json_text",
		ServiceDefinitionID:    "app_gitea",
		ServiceInstallationID:  "svcinst_json_text",
		ServiceMemberID:        "svcmem_json_text",
		ServiceConnectionID:    "svcconn_json_text",
		ServiceConnectionRef:   "acme/agentteam",
		ServiceTargetBindingID: "svctb_json_text",
		ServiceKind:            KindApp,
		SubscriptionKind:       SubscriptionKindPullRequest,
		TargetKind:             "channel",
		TargetRef:              "chn_json_text",
		DeliveryScopeID:        "chn_json_text",
		PermissionCheckScopeID: "chn_json_text",
		OwnerMemberID:          "mem_owner",
		AuthorizerMemberID:     "mem_authorizer",
		CreatedByActorType:     "agent",
		CreatedByActorID:       "agt_json_text",
		CreatedVia:             "mcp",
		ExternalResourceType:   "pull_request",
		ExternalResourceKey:    "gitea:acme/agentteam#42",
		ExternalResourceRef:    "https://gitea.example.com/acme/agentteam/pulls/42",
		ExternalResourceURL:    "https://gitea.example.com/acme/agentteam/pulls/42",
		EventFilterKey:         "filter-key",
		EventFilters:           []byte(`{"events":["comments"]}`),
		DeliveryPolicy:         "app_event_for_principal",
		NotifyPrincipal:        true,
		Status:                 EventSubscriptionStatusActive,
		ActiveSubscriptionKey:  "active-json-text",
		Metadata:               []byte(`{"provider_connection_ref":"acme/agentteam","base_branch":"main"}`),
		Now:                    now,
	})

	require.ErrorContains(t, err, stopAfterInsert.Error())
	require.Len(t, execer.execArgs, 1)
	args := execer.execArgs[0]
	require.Len(t, args, 33)
	require.Equal(t, `{"events":["comments"]}`, args[24])
	require.IsType(t, "", args[24])
	require.Equal(t, `{"provider_connection_ref":"acme/agentteam","base_branch":"main"}`, args[31])
	require.IsType(t, "", args[31])
}

type eventSubscriptionInsertCapture struct {
	execErr  error
	execArgs [][]any
}

func (capture *eventSubscriptionInsertCapture) QueryRow(context.Context, string, ...any) coredb.Row {
	return eventSubscriptionInsertCaptureRow{err: sql.ErrNoRows}
}

func (capture *eventSubscriptionInsertCapture) Query(context.Context, string, ...any) (coredb.Rows, error) {
	return nil, errors.New("unexpected query")
}

func (capture *eventSubscriptionInsertCapture) Exec(_ context.Context, _ string, args ...any) (coredb.Result, error) {
	capture.execArgs = append(capture.execArgs, append([]any(nil), args...))
	if capture.execErr != nil {
		return nil, capture.execErr
	}
	return eventSubscriptionInsertCaptureResult{}, nil
}

type eventSubscriptionInsertCaptureRow struct {
	err error
}

func (row eventSubscriptionInsertCaptureRow) Scan(...any) error {
	return row.err
}

type eventSubscriptionInsertCaptureResult struct{}

func (eventSubscriptionInsertCaptureResult) RowsAffected() int64 {
	return 1
}
