package workspace

import (
	"context"
	"database/sql"
	"errors"
	"regexp"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

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

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

	require.Equal(t, "my-workspace-2026", slugifyName("  My Workspace 2026!!  "))
	require.Equal(t, "alpha-beta", slugifyName("Alpha---Beta"))
	require.Equal(t, "", slugifyName("!!!"))
}

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

	slug, err := slugWithUserSuffix("alice-workspace", "usr_0123456789")
	require.NoError(t, err)
	require.Equal(t, "alice-workspace-23456789", slug)
}

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

	slug, err := slugWithUserSuffix("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij", "usr_12345678")
	require.NoError(t, err)
	require.Len(t, slug, 63)
	require.Equal(t, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab-12345678", slug)
}

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

	require.Equal(t, "Alice's Organization", personalOrganizationName("Alice's Workspace"))
	require.Equal(t, "Project One Organization", personalOrganizationName("Project One"))
	require.Equal(t, "Personal Organization", personalOrganizationName("  "))
}

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

	ctx := context.Background()
	db := &bootstrapLegacyTableBlockerDB{t: t}
	tx, err := db.BeginTx(ctx, coredb.TxOptions{})
	require.NoError(t, err)

	summary, err := create(ctx, tx, model.User{
		ID:        "usr_legacy_write_blocker",
		Email:     "legacy-write-blocker@example.com",
		CreatedAt: time.Now().UTC(),
		UpdatedAt: time.Now().UTC(),
	}, createOptions{
		kind:              "workspace",
		organizationID:    "org_legacy_write_blocker",
		name:              "Legacy Write Blocker",
		slug:              "legacy-write-blocker",
		workspaceGroupID:  "workspace-default",
		defaultAllMessage: "Everyone in the workspace.",
		validateSlug:      func(string) error { return nil },
	})
	require.NoError(t, err)
	require.NotEmpty(t, summary.WorkspaceID)
	require.True(t, db.executedSQLWriteTo("workspace_placements"))
	require.True(t, db.executedSQLWriteTo("scope_activity_workspace_counters"))
	require.False(t, db.executedSQLWriteTo("workflow_task_event_cursors"))
	require.True(t, db.executedSQLWriteTo("scope_memberships"))
	require.True(t, db.executedSQLWriteTo("scope_notification_settings"))
	require.True(t, db.executedSQLWriteTo("scope_channel_profiles"))
}

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

	ctx := context.Background()
	db := &bootstrapLegacyTableBlockerDB{t: t}
	tx, err := db.BeginTx(ctx, coredb.TxOptions{})
	require.NoError(t, err)

	_, err = create(ctx, tx, model.User{
		ID:        "usr_missing_group",
		Email:     "missing-group@example.com",
		CreatedAt: time.Now().UTC(),
		UpdatedAt: time.Now().UTC(),
	}, createOptions{
		kind:              "workspace",
		organizationID:    "org_missing_group",
		name:              "Missing Group",
		slug:              "missing-group",
		defaultAllMessage: "Everyone in the workspace.",
		validateSlug:      func(string) error { return nil },
	})
	require.EqualError(t, err, "workspace placement group id is required")
	require.False(t, db.executedSQLWriteTo("workspaces"))
}

type bootstrapLegacyTableBlockerDB struct {
	t       *testing.T
	queries []string
}

func (db *bootstrapLegacyTableBlockerDB) QueryRow(context.Context, string, ...any) coredb.Row {
	return bootstrapErrorRow{err: sql.ErrNoRows}
}

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

func (db *bootstrapLegacyTableBlockerDB) Exec(_ context.Context, query string, _ ...any) (coredb.Result, error) {
	db.queries = append(db.queries, query)
	if referencesWorkspaceBootstrapLegacyTable(query) {
		db.t.Fatalf("workspace bootstrap should not write legacy scope tables: %s", query)
	}
	return bootstrapRowsAffectedResult(1), nil
}

func (db *bootstrapLegacyTableBlockerDB) BeginTx(context.Context, coredb.TxOptions) (coredb.Tx, error) {
	return bootstrapLegacyTableBlockerTx{db: db}, nil
}

func (db *bootstrapLegacyTableBlockerDB) executedSQLWriteTo(tableName string) bool {
	pattern := regexp.MustCompile(`\b(insert\s+(ignore\s+)?into|update|delete\s+from)\s+` + regexp.QuoteMeta(strings.ToLower(tableName)) + `\b`)
	for _, query := range db.queries {
		normalized := strings.ToLower(query)
		normalized = strings.Join(strings.Fields(normalized), " ")
		if pattern.MatchString(normalized) {
			return true
		}
	}
	return false
}

type bootstrapLegacyTableBlockerTx struct {
	db *bootstrapLegacyTableBlockerDB
}

func (tx bootstrapLegacyTableBlockerTx) QueryRow(ctx context.Context, query string, args ...any) coredb.Row {
	return tx.db.QueryRow(ctx, query, args...)
}

func (tx bootstrapLegacyTableBlockerTx) Query(ctx context.Context, query string, args ...any) (coredb.Rows, error) {
	return tx.db.Query(ctx, query, args...)
}

func (tx bootstrapLegacyTableBlockerTx) Exec(ctx context.Context, query string, args ...any) (coredb.Result, error) {
	return tx.db.Exec(ctx, query, args...)
}

func (tx bootstrapLegacyTableBlockerTx) Commit(context.Context) error {
	return nil
}

func (tx bootstrapLegacyTableBlockerTx) Rollback(context.Context) error {
	return nil
}

type bootstrapErrorRow struct {
	err error
}

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

type bootstrapRowsAffectedResult int64

func (result bootstrapRowsAffectedResult) RowsAffected() int64 {
	return int64(result)
}

func referencesWorkspaceBootstrapLegacyTable(query string) bool {
	normalized := strings.ToLower(query)
	normalized = strings.Join(strings.Fields(normalized), " ")
	return regexp.MustCompile(`\b(insert\s+(ignore\s+)?into|update|delete\s+from)\s+(spaces|channel_profiles|space_participants|space_subscriptions|channels|threads)\b`).MatchString(normalized)
}
