package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/go-sql-driver/mysql"
	"github.com/stretchr/testify/require"

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

func TestRunInitRejectsInvalidEmbeddingDimensionsBeforeDatabaseSideEffects(t *testing.T) {
	root := t.TempDir()
	agentteamPath := filepath.Join(root, "agentteam.db")
	meshxPath := filepath.Join(root, "meshx.db")
	agsPath := filepath.Join(root, "ags.db")
	t.Setenv("AGENTTEAM_DATABASE_DSN", "file:"+agentteamPath)
	t.Setenv("MESHX_DATABASE_DSN", "file:"+meshxPath)
	t.Setenv("MESHX_AGS_DATABASE_DSN", "file:"+agsPath)
	t.Setenv("MESHX_AGS_EMBEDDING_DIMENSIONS", "16385")
	t.Setenv("AGENTTEAM_MESHX_ENABLED", "true")

	err := runInit(context.Background())
	require.ErrorContains(t, err, "MESHX_AGS_EMBEDDING_DIMENSIONS")
	for _, path := range []string{agentteamPath, meshxPath, agsPath} {
		_, statErr := os.Stat(path)
		require.True(t, os.IsNotExist(statErr), "invalid config created database %s: %v", path, statErr)
	}
}

func TestRunInitSkipsMeshXValidationWhenDisabled(t *testing.T) {
	t.Setenv("AGENTTEAM_DATABASE_DSN", "")
	t.Setenv("AGENTTEAM_CORE_STORAGE_DSN", "")
	t.Setenv("AGENTTEAM_MESHX_ENABLED", "false")
	t.Setenv("MESHX_AGS_EMBEDDING_DIMENSIONS", "16385")

	err := runInit(context.Background())
	require.EqualError(t, err, "AGENTTEAM_DATABASE_DSN is required")
}

func TestMeshXSchemaEnabledRejectsInvalidValue(t *testing.T) {
	t.Setenv("AGENTTEAM_MESHX_ENABLED", "sometimes")

	_, err := meshXSchemaEnabled()
	require.EqualError(t, err, "AGENTTEAM_MESHX_ENABLED must be true or false")
}

func TestCurrentDatabaseURLRequiresCoreStorageDSN(t *testing.T) {
	t.Setenv("AGENTTEAM_DATABASE_DSN", "")
	t.Setenv("AGENTTEAM_CORE_STORAGE_DSN", "")

	_, err := currentDatabaseURL()
	require.EqualError(t, err, "AGENTTEAM_DATABASE_DSN is required")
}

func TestOpenAdminDBForInitCreatesMissingTiDBDatabaseAfterUnknownDatabaseError(t *testing.T) {
	tidbAdmin := coredb.NewAdmin()

	expectedDB := &stubAdminDB{}
	openCalls := 0
	created := false

	admin, db, err := openAdminDBForInit(
		context.Background(),
		tidbAdmin,
		"root@tcp(127.0.0.1:4000)/agentteam?parseTime=true",
		5*time.Second,
		func(context.Context, coredb.Admin, string, time.Duration) (coredb.AdminDB, error) {
			openCalls++
			if openCalls == 1 {
				return nil, fmt.Errorf("ping tidb: %w", &mysql.MySQLError{Number: 1049, Message: "Unknown database 'agentteam'"})
			}
			return expectedDB, nil
		},
		func(context.Context, string, time.Duration) error {
			created = true
			return nil
		},
	)
	require.NoError(t, err)
	require.Equal(t, tidbAdmin, admin)
	require.Same(t, expectedDB, db)
	require.True(t, created)
	require.Equal(t, 2, openCalls)
}

func TestOpenAdminDBForInitDoesNotCreateDatabaseForNonUnknownDatabaseError(t *testing.T) {
	tidbAdmin := coredb.NewAdmin()

	created := false
	_, _, err := openAdminDBForInit(
		context.Background(),
		tidbAdmin,
		"root@tcp(127.0.0.1:4000)/agentteam?parseTime=true",
		5*time.Second,
		func(context.Context, coredb.Admin, string, time.Duration) (coredb.AdminDB, error) {
			return nil, fmt.Errorf("ping tidb: %w", &mysql.MySQLError{Number: 1045, Message: "Access denied"})
		},
		func(context.Context, string, time.Duration) error {
			created = true
			return nil
		},
	)
	require.EqualError(t, err, "connect core main-path database: ping tidb: Error 1045: Access denied")
	require.False(t, created)
}

func TestEnsureTiDBDatabaseExistsRequiresDatabaseName(t *testing.T) {
	err := ensureTiDBDatabaseExists(
		context.Background(),
		"root@tcp(127.0.0.1:4000)/?parseTime=true",
		5*time.Second,
	)
	require.EqualError(t, err, "tidb dsn must include a database name")
}

type stubAdminDB struct{}

func (stubAdminDB) Ping(context.Context) error { return nil }

func (stubAdminDB) Close() {}
