package trace

import (
	"context"
	"os"
	"strings"
	"testing"
	"time"

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

func TestTracePostgreSQLLifecycle(t *testing.T) {
	databaseURL := os.Getenv("TRACE_TEST_DATABASE_URL")
	if databaseURL == "" {
		t.Skip("TRACE_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()
	store := NewStore(pool)
	_, _ = pool.Exec(ctx, `DELETE FROM gateway.agent_traces WHERE request_id LIKE 'trace-integration%'`)
	item, err := store.Start(ctx, StartInput{RequestID: "trace-integration", TraceType: "application", TargetID: "77777777-7777-4777-8777-777777777777", TargetCode: "trace_app", ConversationID: "conversation-1", Metadata: map[string]any{"version": 1}})
	if err != nil {
		t.Fatal(err)
	}
	defer pool.Exec(ctx, `DELETE FROM gateway.agent_traces WHERE request_id LIKE 'trace-integration%'`)
	model, err := store.StartSpan(ctx, SpanInput{TraceID: item.ID, SpanType: "model", Name: "chat.completions", Round: 0, ProviderCode: "test", Model: "test-model"})
	if err != nil {
		t.Fatal(err)
	}
	if err = store.FinishSpan(ctx, model.ID, SpanFinishInput{Status: "success", InputTokens: 12, OutputTokens: 8, Metadata: map[string]any{"http_status": 200}}); err != nil {
		t.Fatal(err)
	}
	tool, err := store.StartSpan(ctx, SpanInput{TraceID: item.ID, SpanType: "tool", Name: "lookup", Round: 0})
	if err != nil {
		t.Fatal(err)
	}
	if err = store.FinishSpan(ctx, tool.ID, SpanFinishInput{Status: "error", Error: "upstream timeout"}); err != nil {
		t.Fatal(err)
	}
	if err = store.Finish(ctx, item.ID, FinishInput{Status: "success", RetrievalCount: 2, ModelCallCount: 1, ToolCallCount: 1}); err != nil {
		t.Fatal(err)
	}
	loaded, err := store.Get(ctx, item.ID)
	if err != nil || loaded.Status != "success" || loaded.ModelCallCount != 1 || len(loaded.Spans) != 2 {
		t.Fatalf("loaded=%+v err=%v", loaded, err)
	}
	if loaded.Spans[0].ProviderCode != "test" || loaded.Spans[0].Model != "test-model" {
		t.Fatalf("model span route metadata was not preserved: %+v", loaded.Spans[0])
	}
	if loaded.LatencyMS == nil || loaded.StartedAt.After(time.Now().UTC().Add(time.Second)) {
		t.Fatalf("invalid timing: %+v", loaded)
	}
	items, err := store.List(ctx, Filter{From: time.Now().Add(-time.Minute), To: time.Now().Add(time.Minute), TargetCode: "trace_app", Limit: 10})
	if err != nil || len(items) != 1 || items[0].ID != item.ID {
		t.Fatalf("list=%+v err=%v", items, err)
	}
	sessions, err := store.ListSessions(ctx, SessionFilter{From: time.Now().Add(-time.Minute), To: time.Now().Add(time.Minute), SessionID: "application:trace_app:conversation-1", Limit: 10})
	if err != nil || len(sessions) != 1 || sessions[0].TraceCount != 1 || sessions[0].LatestTraceID != item.ID || sessions[0].ModelCallCount != 1 {
		t.Fatalf("sessions=%+v err=%v", sessions, err)
	}
	digital, err := store.Start(ctx, StartInput{RequestID: "trace-integration-digital", TraceType: "digital_employee", TargetID: "88888888-8888-4888-8888-888888888888", TargetCode: "trace_employee", ConversationID: "conversation-1"})
	if err != nil {
		t.Fatal(err)
	}
	if err = store.Finish(ctx, digital.ID, FinishInput{Status: "success", ModelCallCount: 1}); err != nil {
		t.Fatal(err)
	}
	allSessions, err := store.ListSessions(ctx, SessionFilter{From: time.Now().Add(-time.Minute), To: time.Now().Add(time.Minute), Limit: 10})
	if err != nil || len(allSessions) < 2 {
		t.Fatalf("all sessions=%+v err=%v", allSessions, err)
	}
	seenTypes := map[string]bool{}
	for _, session := range allSessions {
		if strings.HasPrefix(session.ID, "application:trace_app:") || strings.HasPrefix(session.ID, "digital_employee:trace_employee:") {
			seenTypes[session.TraceType] = true
		}
	}
	if !seenTypes["application"] || !seenTypes["digital_employee"] {
		t.Fatalf("session types=%v", seenTypes)
	}
}
