package agentnode

import (
	"context"
	"net"
	"os"
	"testing"
	"time"

	"aigateway.local/core/internal/platform/config"
	"aigateway.local/core/internal/platform/database"
)

func TestAgentNodePostgreSQLLifecycle(t *testing.T) {
	databaseURL := os.Getenv("AGENT_NODE_TEST_DATABASE_URL")
	if databaseURL == "" {
		t.Skip("AGENT_NODE_TEST_DATABASE_URL is not set")
	}
	ctx := context.Background()
	pool, err := database.Open(ctx, config.Database{URL: databaseURL, MaxConns: 4, MinConns: 0})
	if err != nil {
		t.Fatal(err)
	}
	defer pool.Close()
	_, _ = pool.Exec(ctx, `DELETE FROM gateway.agent_nodes WHERE code='node-integration'`)
	defer pool.Exec(ctx, `DELETE FROM gateway.agent_nodes WHERE code='node-integration'`)
	store := NewStore(pool)
	node, token, err := store.Create(ctx, CreateInput{Code: "node-integration", Name: "Integration Node", PoolType: "private", PoolCode: "test", Enabled: true}, "")
	if err != nil {
		t.Fatal(err)
	}
	if token == "" || node.Status != "pending" || node.TokenPrefix == "" {
		t.Fatalf("created node=%+v token=%q", node, token)
	}
	updated, err := store.Update(ctx, UpdateInput{ID: node.ID, Name: "Integration Node v2", Description: "test", Endpoint: "https://node.invalid", NodeType: "executor", PoolType: "public", PoolCode: "shared", Enabled: true})
	if err != nil || updated.Name != "Integration Node v2" || updated.PoolType != "public" {
		t.Fatalf("updated node=%+v err=%v", updated, err)
	}
	online, err := store.Heartbeat(ctx, node.Code, token, net.ParseIP("192.0.2.10"), HeartbeatInput{Version: "0.10.0-node", Capabilities: map[string]any{"tool_exec": true}, Metadata: map[string]any{"region": "test"}})
	if err != nil || online.Status != "online" || online.Version != "0.10.0-node" || online.LastHeartbeatIP != "192.0.2.10" {
		t.Fatalf("heartbeat node=%+v err=%v", online, err)
	}
	preview, err := store.PreviewRoute(ctx, RoutePreviewInput{PoolType: "public", PoolCode: "shared", RequestKey: "integration-request", RequiredCapabilities: []string{"tool_exec"}})
	if err != nil || preview.Reason != "selected_online_node" || preview.Selected == nil || preview.Selected.ID != online.ID || len(preview.Candidates) != 1 {
		t.Fatalf("route preview=%+v err=%v", preview, err)
	}
	rotated, newToken, err := store.RotateToken(ctx, node.ID)
	if err != nil || newToken == token || rotated.TokenPrefix == node.TokenPrefix {
		t.Fatalf("rotated node=%+v token=%q err=%v", rotated, newToken, err)
	}
	if _, err = store.Heartbeat(ctx, node.Code, token, nil, HeartbeatInput{}); err != ErrInvalidToken {
		t.Fatalf("old token err=%v", err)
	}
	if _, err = store.Heartbeat(ctx, node.Code, newToken, nil, HeartbeatInput{}); err != nil {
		t.Fatal(err)
	}
	items, err := store.List(ctx)
	if err != nil || len(items) == 0 || items[0].UpdatedAt.Before(time.Now().UTC().Add(-time.Minute)) {
		t.Fatalf("items=%+v err=%v", items, err)
	}
}
