//go:build e2e

package integration_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"path/filepath"
	"testing"
	"time"

	"github.com/DeBrosOfficial/network/e2e"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestFullStack_GoAPI_SQLite(t *testing.T) {
	env, err := e2e.LoadTestEnv()
	require.NoError(t, err, "Failed to load test environment")

	appName := fmt.Sprintf("fullstack-app-%d", time.Now().Unix())
	backendName := appName + "-backend"
	dbName := appName + "-db"

	var backendID string

	defer func() {
		if !env.SkipCleanup {
			if backendID != "" {
				e2e.DeleteDeployment(t, env, backendID)
			}
			e2e.DeleteSQLiteDB(t, env, dbName)
		}
	}()

	// Step 1: Create SQLite database
	t.Run("Create SQLite database", func(t *testing.T) {
		e2e.CreateSQLiteDB(t, env, dbName)

		// Create users table
		query := `CREATE TABLE users (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			name TEXT NOT NULL,
			email TEXT UNIQUE NOT NULL,
			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
		)`
		e2e.ExecuteSQLQuery(t, env, dbName, query)

		// Insert test data
		insertQuery := `INSERT INTO users (name, email) VALUES ('Alice', 'alice@example.com')`
		result := e2e.ExecuteSQLQuery(t, env, dbName, insertQuery)

		assert.NotNil(t, result, "Should execute INSERT successfully")
		t.Logf("✓ Database created with users table")
	})

	// Step 2: Deploy Go backend (this would normally connect to SQLite)
	// Note: For now we test the Go backend deployment without actual DB connection
	// as that requires environment variable injection during deployment
	t.Run("Deploy Go backend", func(t *testing.T) {
		tarballPath := filepath.Join("../../testdata/apps/go-api")

		// Note: In a real implementation, we would pass DATABASE_NAME env var
		// For now, we just test the deployment mechanism
		backendID = e2e.CreateTestDeployment(t, env, backendName, tarballPath)

		assert.NotEmpty(t, backendID, "Backend deployment ID should not be empty")
		t.Logf("✓ Go backend deployed: %s", backendName)

		// Wait for deployment to become active
		time.Sleep(3 * time.Second)
	})

	// Step 3: Test database operations
	t.Run("Test database CRUD operations", func(t *testing.T) {
		// INSERT
		insertQuery := `INSERT INTO users (name, email) VALUES ('Bob', 'bob@example.com')`
		e2e.ExecuteSQLQuery(t, env, dbName, insertQuery)

		// SELECT
		users := e2e.QuerySQLite(t, env, dbName, "SELECT * FROM users ORDER BY id")
		require.GreaterOrEqual(t, len(users), 2, "Should have at least 2 users")

		assert.Equal(t, "Alice", users[0]["name"], "First user should be Alice")
		assert.Equal(t, "Bob", users[1]["name"], "Second user should be Bob")

		t.Logf("✓ Database CRUD operations work")
		t.Logf("   - Found %d users", len(users))

		// UPDATE
		updateQuery := `UPDATE users SET email = 'alice.new@example.com' WHERE name = 'Alice'`
		result := e2e.ExecuteSQLQuery(t, env, dbName, updateQuery)

		rowsAffected, ok := result["rows_affected"].(float64)
		require.True(t, ok, "Should have rows_affected")
		assert.Equal(t, float64(1), rowsAffected, "Should update 1 row")

		// Verify update
		updated := e2e.QuerySQLite(t, env, dbName, "SELECT email FROM users WHERE name = 'Alice'")
		require.Len(t, updated, 1, "Should find Alice")
		assert.Equal(t, "alice.new@example.com", updated[0]["email"], "Email should be updated")

		t.Logf("✓ UPDATE operation verified")

		// DELETE
		deleteQuery := `DELETE FROM users WHERE name = 'Bob'`
		result = e2e.ExecuteSQLQuery(t, env, dbName, deleteQuery)

		rowsAffected, ok = result["rows_affected"].(float64)
		require.True(t, ok, "Should have rows_affected")
		assert.Equal(t, float64(1), rowsAffected, "Should delete 1 row")

		// Verify deletion
		remaining := e2e.QuerySQLite(t, env, dbName, "SELECT * FROM users")
		assert.Equal(t, 1, len(remaining), "Should have 1 user remaining")

		t.Logf("✓ DELETE operation verified")
	})

	// Step 4: Test backend API endpoints (if deployment is active)
	t.Run("Test backend API endpoints", func(t *testing.T) {
		deployment := e2e.GetDeployment(t, env, backendID)

		status, ok := deployment["status"].(string)
		if !ok || status != "active" {
			t.Skip("Backend deployment not active, skipping API tests")
			return
		}

		backendDomain := env.BuildDeploymentDomain(backendName)

		// Test health endpoint
		resp := e2e.TestDeploymentWithHostHeader(t, env, backendDomain, "/health")
		defer resp.Body.Close()

		if resp.StatusCode == http.StatusOK {
			var health map[string]interface{}
			bodyBytes, _ := io.ReadAll(resp.Body)
			require.NoError(t, json.Unmarshal(bodyBytes, &health), "Should decode health response")

			assert.Equal(t, "healthy", health["status"], "Status should be healthy")
			assert.Equal(t, "go-backend-test", health["service"], "Service name should match")

			t.Logf("✓ Backend health check passed")
		} else {
			t.Logf("⚠ Health check returned status %d (deployment may still be starting)", resp.StatusCode)
		}

		// Test users API endpoint
		resp2 := e2e.TestDeploymentWithHostHeader(t, env, backendDomain, "/api/users")
		defer resp2.Body.Close()

		if resp2.StatusCode == http.StatusOK {
			var usersResp map[string]interface{}
			bodyBytes, _ := io.ReadAll(resp2.Body)
			require.NoError(t, json.Unmarshal(bodyBytes, &usersResp), "Should decode users response")

			users, ok := usersResp["users"].([]interface{})
			require.True(t, ok, "Should have users array")
			assert.GreaterOrEqual(t, len(users), 3, "Should have test users")

			t.Logf("✓ Backend API endpoint works")
			t.Logf("   - Users endpoint returned %d users", len(users))
		} else {
			t.Logf("⚠ Users API returned status %d (deployment may still be starting)", resp2.StatusCode)
		}
	})

	// Step 5: Test database backup
	t.Run("Test database backup", func(t *testing.T) {
		reqBody := map[string]string{"database_name": dbName}
		bodyBytes, _ := json.Marshal(reqBody)

		req, _ := http.NewRequest("POST", env.GatewayURL+"/v1/db/sqlite/backup", bytes.NewReader(bodyBytes))
		req.Header.Set("Authorization", "Bearer "+env.APIKey)
		req.Header.Set("Content-Type", "application/json")

		resp, err := env.HTTPClient.Do(req)
		require.NoError(t, err, "Should execute backup request")
		defer resp.Body.Close()

		if resp.StatusCode == http.StatusOK {
			var result map[string]interface{}
			bodyBytes, _ := io.ReadAll(resp.Body)
			require.NoError(t, json.Unmarshal(bodyBytes, &result), "Should decode backup response")

			backupCID, ok := result["backup_cid"].(string)
			require.True(t, ok, "Should have backup CID")
			assert.NotEmpty(t, backupCID, "Backup CID should not be empty")

			t.Logf("✓ Database backup created")
			t.Logf("   - CID: %s", backupCID)
		} else {
			bodyBytes, _ := io.ReadAll(resp.Body)
			t.Logf("⚠ Backup returned status %d: %s", resp.StatusCode, string(bodyBytes))
		}
	})

	// Step 6: Test concurrent database queries
	t.Run("Test concurrent database reads", func(t *testing.T) {
		// WAL mode should allow concurrent reads — run sequentially to avoid t.Fatal in goroutines
		for i := 0; i < 5; i++ {
			users := e2e.QuerySQLite(t, env, dbName, "SELECT * FROM users")
			assert.GreaterOrEqual(t, len(users), 0, "Should query successfully")
		}

		t.Logf("✓ Sequential reads successful")
	})
}

func TestFullStack_StaticSite_SQLite(t *testing.T) {
	env, err := e2e.LoadTestEnv()
	require.NoError(t, err, "Failed to load test environment")

	appName := fmt.Sprintf("fullstack-static-%d", time.Now().Unix())
	frontendName := appName + "-frontend"
	dbName := appName + "-db"

	var frontendID string

	defer func() {
		if !env.SkipCleanup {
			if frontendID != "" {
				e2e.DeleteDeployment(t, env, frontendID)
			}
			e2e.DeleteSQLiteDB(t, env, dbName)
		}
	}()

	t.Run("Deploy static site and create database", func(t *testing.T) {
		// Create database
		e2e.CreateSQLiteDB(t, env, dbName)
		e2e.ExecuteSQLQuery(t, env, dbName, "CREATE TABLE page_views (id INTEGER PRIMARY KEY, page TEXT, count INTEGER)")
		e2e.ExecuteSQLQuery(t, env, dbName, "INSERT INTO page_views (page, count) VALUES ('home', 0)")

		// Deploy frontend
		tarballPath := filepath.Join("../../testdata/apps/react-app")
		frontendID = e2e.CreateTestDeployment(t, env, frontendName, tarballPath)

		assert.NotEmpty(t, frontendID, "Frontend deployment should succeed")
		t.Logf("✓ Static site deployed with SQLite backend")

		// Wait for deployment
		time.Sleep(2 * time.Second)
	})

	t.Run("Test frontend serving and database interaction", func(t *testing.T) {
		frontendDomain := env.BuildDeploymentDomain(frontendName)

		// Test frontend
		resp := e2e.TestDeploymentWithHostHeader(t, env, frontendDomain, "/")
		defer resp.Body.Close()

		assert.Equal(t, http.StatusOK, resp.StatusCode, "Frontend should serve")

		body, _ := io.ReadAll(resp.Body)
		assert.Contains(t, string(body), "<div id=\"root\">", "Should contain React app")

		// Simulate page view tracking
		e2e.ExecuteSQLQuery(t, env, dbName, "UPDATE page_views SET count = count + 1 WHERE page = 'home'")

		// Verify count
		views := e2e.QuerySQLite(t, env, dbName, "SELECT count FROM page_views WHERE page = 'home'")
		require.Len(t, views, 1, "Should have page view record")

		count, ok := views[0]["count"].(float64)
		require.True(t, ok, "Count should be a number")
		assert.Equal(t, float64(1), count, "Page view count should be incremented")

		t.Logf("✓ Full stack integration verified")
		t.Logf("   - Frontend: %s", frontendDomain)
		t.Logf("   - Database: %s", dbName)
		t.Logf("   - Page views tracked: %.0f", count)
	})
}
