package agent_test

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/api/testkit"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	coredbtestkit "github.com/tidbcloud/AgentTeam/internal/coredb/testkit"
	"github.com/tidbcloud/AgentTeam/internal/service/agentruntime"
	"github.com/tidbcloud/AgentTeam/internal/store/taskboard"
)

func newAgentRuntime(_ coredb.DB, dataStore *testkit.Store) *agentruntime.Service {
	return agentruntime.New(agentruntime.Dependencies{
		Agents:   dataStore.Agent(),
		Messages: dataStore.Message(),
		Recovery: dataStore.Rewind(),
	})
}

func mustTaskClient(t *testing.T, pool coredb.DB) *taskboard.Store {
	t.Helper()
	return taskboard.New(pool)
}

var agentAPITestRuntimeFixture = coredbtestkit.NewTiDBRuntimeFixture(coredbtestkit.TiDBTestRuntimeUnistore)

func TestMain(m *testing.M) {
	os.Exit(coredbtestkit.RunTestsWithTiDBRuntimeFixture(m, "internal/api/agent", agentAPITestRuntimeFixture))
}

func agentAPITestRootDSN(t testing.TB) string {
	t.Helper()
	return agentAPITestRuntimeFixture.RootDSN(t)
}

func startAgentAPITestPool(t *testing.T, ctx context.Context) (string, coredb.DB) {
	t.Helper()
	_ = ctx
	databaseURL := coredbtestkit.StartInitializedTiDBWithRootDSN(t, agentAPITestRootDSN(t))
	return databaseURL, coredb.WrapSQLDB(coredbtestkit.OpenTiDB(t, databaseURL))
}

func doAgentRequest(t *testing.T, router http.Handler, agentID string, token string, path string, payload map[string]any) *httptest.ResponseRecorder {
	t.Helper()

	body, err := json.Marshal(payload)
	require.NoError(t, err)

	request := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(body))
	request.Header.Set("Content-Type", "application/json")
	request.Header.Set("Authorization", "Bearer "+token)
	request.Header.Set("X-Agent-Id", agentID)

	response := httptest.NewRecorder()
	router.ServeHTTP(response, request)
	return response
}

func mustExec(t *testing.T, pool coredb.QueryExecer, query string, args ...any) {
	t.Helper()

	_, err := pool.Exec(context.Background(), query, args...)
	require.NoError(t, err)
	if strings.Contains(query, "INSERT INTO machines") && len(args) > 0 {
		machineID, ok := args[0].(string)
		require.True(t, ok, "machine insert helper expects first arg to be machine id")
		_, err = pool.Exec(context.Background(), `
UPDATE machines
SET daemon_version = 'agentteam/v1.0.2-rc.11'
WHERE id = $1
`, machineID)
		require.NoError(t, err)
	}
	syncScopeAgentAPITestFacts(t, pool)
}

func syncScopeAgentAPITestFacts(t *testing.T, pool coredb.QueryExecer) {
	t.Helper()

	ctx := context.Background()
	now := time.Now().UTC()
	_, err := pool.Exec(ctx, `
INSERT IGNORE INTO scope_notification_settings (
    scope_id, member_id, state, created_at, updated_at
)
SELECT
    sm.scope_id,
    sm.member_id,
    CASE WHEN m.type = 'agent' THEN 'auto-wake' ELSE 'active' END,
    COALESCE(sm.created_at, $1),
    COALESCE(sm.updated_at, sm.created_at, $1)
FROM scope_memberships sm
JOIN members m ON m.id = sm.member_id
`, now)
	require.NoError(t, err)
}

func seedAgentTestWorkspaceOrganization(t *testing.T, pool coredb.QueryExecer, workspaceID string, slug string, createdByUserID string, now time.Time) string {
	t.Helper()

	organizationID := "org_" + workspaceID
	mustExec(t, pool, `
INSERT INTO organizations (id, slug, name, created_by_user_id, created_at, updated_at)
VALUES ($1, $2, $2, $3, $4, $4)
`, organizationID, slug, createdByUserID, now)
	return organizationID
}
