package tests

import (
	"bytes"
	"net/http"
	"net/http/httptest"
	"testing"

	"veza-backend-api/internal/api"
	"veza-backend-api/internal/config"
	"veza-backend-api/internal/database"
	"veza-backend-api/internal/eventbus" // Added
	"veza-backend-api/internal/metrics" // Added

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"go.uber.org/zap/zaptest"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"github.com/redis/go-redis/v9" // Added
)

// Helper function to create a test Gin engine with routes set up
func setupTestRouter(t *testing.T) (*gin.Engine, func()) {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	logger := zaptest.NewLogger(t)

	// Create a minimal mock *gorm.DB instance
	// This avoids AutoMigrate failures with PostgreSQL-specific DDL in SQLite.
	// We're essentially mocking the DB connection for the routes that don't need real persistence.
	mockGormDB, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{})
	assert.NoError(t, err)

	mockDB := &database.Database{
		GormDB: mockGormDB,
		Logger: logger,
	}

	// Mock Config
	mockConfig := &config.Config{
		AppPort:         8080,
		CORSOrigins:     []string{"*"},
		JWTSecret:       "test-secret",
		UploadDir:       "uploads/test",
		StreamServerURL: "http://localhost:8000",
		Database:        mockDB, // Corrected from testDB
		Logger:          logger, // Pass the logger to the config
		RedisClient:     &redis.Client{}, // Provide a dummy RedisClient
		RabbitMQEventBus: &eventbus.RabbitMQEventBus{}, // Provide a dummy RabbitMQEventBus
		ErrorMetrics:    metrics.NewErrorMetrics(), // Initialize ErrorMetrics
	}

	apiRouter := api.NewAPIRouter(mockDB, mockConfig)
	apiRouter.Setup(router)

	cleanup := func() {
		// No specific cleanup needed for in-memory SQLite in this setup
		// GORM closes the DB when the gormDB object is garbage collected.
	}

	return router, cleanup
}

func TestPublicCoreRoutes(t *testing.T) {
	router, cleanup := setupTestRouter(t)
	defer cleanup()

	// Define test cases for public core routes
	testCases := []struct {
		name                   string
		method                 string
		legacyPath             string
		modernPath             string
		expectedStatus         int
		expectDeprecatedHeader bool
	}{
		{
			name:           "Health Check",
			method:         http.MethodGet,
			legacyPath:     "/health",
			modernPath:     "/api/v1/health",
			expectedStatus: http.StatusOK,
			expectDeprecatedHeader: true,
		},
		{
			name:           "Liveness Check",
			method:         http.MethodGet,
			legacyPath:     "/healthz",
			modernPath:     "/api/v1/healthz",
			expectedStatus: http.StatusOK,
			expectDeprecatedHeader: true,
		},
		{
			name:           "Readiness Check",
			method:         http.MethodGet,
			legacyPath:     "/readyz",
			modernPath:     "/api/v1/readyz",
			expectedStatus: http.StatusOK,
			expectDeprecatedHeader: true,
		},
		// Metrics endpoints might return different body content due to dynamic nature,
		// so we primarily check status code.
		{
			name:           "Metrics",
			method:         http.MethodGet,
			legacyPath:     "/metrics",
			modernPath:     "/api/v1/metrics",
			expectedStatus: http.StatusOK,
			expectDeprecatedHeader: true,
		},
		{
			name:           "Aggregated Metrics",
			method:         http.MethodGet,
			legacyPath:     "/metrics/aggregated",
			modernPath:     "/api/v1/metrics/aggregated",
			expectedStatus: http.StatusOK,
			expectDeprecatedHeader: true,
		},
		{
			name:           "System Metrics",
			method:         http.MethodGet,
			legacyPath:     "/system/metrics",
			modernPath:     "/api/v1/system/metrics",
			expectedStatus: http.StatusOK,
			expectDeprecatedHeader: true,
		},
	}

	for _, tc := range testCases {
		t.Run("Legacy "+tc.name, func(t *testing.T) {
			req, _ := http.NewRequest(tc.method, tc.legacyPath, nil)
			w := httptest.NewRecorder()
			router.ServeHTTP(w, req)

			assert.Equal(t, tc.expectedStatus, w.Code)
			if tc.expectDeprecatedHeader {
				assert.Contains(t, w.Header().Get("Deprecated"), "true")
			}
		})

		t.Run("Modern "+tc.name, func(t *testing.T) {
			req, _ := http.NewRequest(tc.method, tc.modernPath, nil)
			w := httptest.NewRecorder()
			router.ServeHTTP(w, req)

			assert.Equal(t, tc.expectedStatus, w.Code)
			assert.NotContains(t, w.Header().Get("Deprecated"), "true") // Modern routes should NOT be deprecated
		})
	}
}

func TestInternalTrackStreamCallbackRoutes(t *testing.T) {
	router, cleanup := setupTestRouter(t)
	defer cleanup()

	// Test case for internal track stream callback
	testCases := []struct {
		name                   string
		method                 string
		legacyPath             string
		modernPath             string
		expectedStatus         int
		expectDeprecatedHeader bool
	}{
		{
			name:           "Track Stream Ready Callback",
			method:         http.MethodPost, // This is a POST request
			legacyPath:     "/internal/tracks/123e4567-e89b-12d3-a456-426614174000/stream-ready", // Example UUID
			modernPath:     "/api/v1/internal/tracks/123e4567-e89b-12d3-a456-426614174000/stream-ready", // Example UUID
			expectedStatus: http.StatusNotFound, // Assuming 404 because track 123 won't exist
			expectDeprecatedHeader: true,
		},
	}

	for _, tc := range testCases {
		t.Run("Legacy "+tc.name, func(t *testing.T) {
			req, _ := http.NewRequest(tc.method, tc.legacyPath, bytes.NewBufferString("{}")) // POST needs a body
			req.Header.Set("Content-Type", "application/json")
			w := httptest.NewRecorder()
			router.ServeHTTP(w, req)

			assert.Equal(t, tc.expectedStatus, w.Code)
			if tc.expectDeprecatedHeader {
				assert.Contains(t, w.Header().Get("Deprecated"), "true")
			}
		})

		t.Run("Modern "+tc.name, func(t *testing.T) {
			req, _ := http.NewRequest(tc.method, tc.modernPath, bytes.NewBufferString("{}")) // POST needs a body
			req.Header.Set("Content-Type", "application/json")
			w := httptest.NewRecorder()
			router.ServeHTTP(w, req)

			assert.Equal(t, tc.expectedStatus, w.Code)
			assert.NotContains(t, w.Header().Get("Deprecated"), "true") // Modern routes should NOT be deprecated
		})
	}
}