package integration

import (
	"context"
	"database/sql"
	"os"
	"testing"
	"time"

	_ "github.com/lib/pq"
	amqp "github.com/rabbitmq/amqp091-go"
	"github.com/rede5/gohorsejobs/backend/internal/dto"
	"github.com/rede5/gohorsejobs/backend/internal/services"
)

func ptrString(s string) *string {
	return &s
}

// TestStorageService_Integration tests StorageService with real S3/Civo credentials
// Run with: go test -v ./tests/integration/... -tags=integration
func TestStorageService_Integration(t *testing.T) {
	// Skip if not running integration tests
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test - set RUN_INTEGRATION_TESTS=true to run")
	}

	// These should be set from environment
	endpoint := os.Getenv("AWS_ENDPOINT")
	accessKey := os.Getenv("AWS_ACCESS_KEY_ID")
	secretKey := os.Getenv("AWS_SECRET_ACCESS_KEY")
	bucket := os.Getenv("S3_BUCKET")
	region := os.Getenv("AWS_REGION")

	if endpoint == "" || accessKey == "" || secretKey == "" || bucket == "" {
		t.Skip("Missing S3 credentials in environment")
	}

	t.Logf("Testing with endpoint: %s, bucket: %s, region: %s", endpoint, bucket, region)

	t.Run("verifies S3 credentials are valid", func(t *testing.T) {
		t.Logf("Credentials loaded successfully")
		t.Logf("  Endpoint: %s", endpoint)
		t.Logf("  Bucket: %s", bucket)
		t.Logf("  Region: %s", region)
		t.Logf("  Access Key: %s...", accessKey[:4])
	})
}

// TestDatabaseConnection_Integration tests real database connectivity
func TestDatabaseConnection_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test - set RUN_INTEGRATION_TESTS=true to run")
	}

	dbURL := os.Getenv("DATABASE_URL")
	if dbURL == "" {
		t.Skip("Missing DATABASE_URL in environment")
	}

	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	t.Run("pings database", func(t *testing.T) {
		err := db.Ping()
		if err != nil {
			t.Fatalf("Failed to ping database: %v", err)
		}
		t.Log("✅ Database connection successful")
	})

	t.Run("queries users table", func(t *testing.T) {
		var count int
		err := db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
		if err != nil {
			t.Fatalf("Failed to query users: %v", err)
		}
		t.Logf("✅ Users table has %d rows", count)
	})

	t.Run("queries jobs table", func(t *testing.T) {
		var count int
		err := db.QueryRow("SELECT COUNT(*) FROM jobs").Scan(&count)
		if err != nil {
			t.Fatalf("Failed to query jobs: %v", err)
		}
		t.Logf("✅ Jobs table has %d rows", count)
	})

	t.Run("queries companies table", func(t *testing.T) {
		var count int
		err := db.QueryRow("SELECT COUNT(*) FROM companies").Scan(&count)
		if err != nil {
			t.Fatalf("Failed to query companies: %v", err)
		}
		t.Logf("✅ Companies table has %d rows", count)
	})
}

// TestSettingsService_Integration tests SettingsService with real database
func TestSettingsService_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test - set RUN_INTEGRATION_TESTS=true to run")
	}

	dbURL := os.Getenv("DATABASE_URL")
	if dbURL == "" {
		t.Skip("Missing DATABASE_URL in environment")
	}

	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	settingsService := services.NewSettingsService(db)
	ctx := context.Background()

	t.Run("saves and retrieves settings", func(t *testing.T) {
		testKey := "integration_test_key"
		testValue := map[string]interface{}{
			"test":      true,
			"timestamp": "2026-01-01",
		}

		// Save
		err := settingsService.SaveSettings(ctx, testKey, testValue)
		if err != nil {
			t.Fatalf("Failed to save settings: %v", err)
		}
		t.Logf("✅ Saved setting '%s'", testKey)

		// Retrieve
		result, err := settingsService.GetSettings(ctx, testKey)
		if err != nil {
			t.Fatalf("Failed to get settings: %v", err)
		}
		if result == nil {
			t.Fatal("Expected result, got nil")
		}
		t.Logf("✅ Retrieved setting '%s': %s", testKey, string(result))

		// Cleanup
		_, err = db.Exec("DELETE FROM system_settings WHERE key = $1", testKey)
		if err != nil {
			t.Logf("Warning: Failed to cleanup test setting: %v", err)
		}
	})
}

// TestJobService_Integration tests JobService with real database
func TestJobService_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test - set RUN_INTEGRATION_TESTS=true to run")
	}

	dbURL := os.Getenv("DATABASE_URL")
	if dbURL == "" {
		t.Skip("Missing DATABASE_URL in environment")
	}

	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	jobService := services.NewJobService(db)

	t.Run("lists jobs", func(t *testing.T) {
		filter := dto.JobFilterQuery{}
		jobs, total, err := jobService.GetJobs(filter)
		if err != nil {
			t.Fatalf("Failed to list jobs: %v", err)
		}
		t.Logf("✅ Listed %d jobs (total: %d)", len(jobs), total)
	})
}

// TestNotificationService_Integration tests NotificationService with real database
func TestNotificationService_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test - set RUN_INTEGRATION_TESTS=true to run")
	}

	dbURL := os.Getenv("DATABASE_URL")
	if dbURL == "" {
		t.Skip("Missing DATABASE_URL in environment")
	}

	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	notificationService := services.NewNotificationService(db, nil)
	ctx := context.Background()

	t.Run("lists notifications for user", func(t *testing.T) {
		// Use a test user ID that might exist
		userID := "00000000-0000-0000-0000-000000000000"
		notifications, err := notificationService.ListNotifications(ctx, userID)
		if err != nil {
			t.Fatalf("Failed to list notifications: %v", err)
		}
		t.Logf("✅ Listed %d notifications for user", len(notifications))
	})
}

// TestAMQPConnection_Integration tests real AMQP/RabbitMQ connectivity
func TestAMQPConnection_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test - set RUN_INTEGRATION_TESTS=true to run")
	}

	amqpURL := os.Getenv("AMQP_URL")
	if amqpURL == "" {
		t.Skip("Missing AMQP_URL in environment")
	}

	t.Run("connects to CloudAMQP", func(t *testing.T) {
		// Import amqp package dynamically
		conn, err := amqp.Dial(amqpURL)
		if err != nil {
			t.Fatalf("Failed to connect to AMQP: %v", err)
		}
		defer conn.Close()

		t.Log("✅ AMQP connection successful")

		ch, err := conn.Channel()
		if err != nil {
			t.Fatalf("Failed to open channel: %v", err)
		}
		defer ch.Close()

		t.Log("✅ AMQP channel opened")

		// Declare a test queue
		q, err := ch.QueueDeclare(
			"integration_test_queue",
			false, // durable
			true,  // delete when unused
			false, // exclusive
			false, // no-wait
			nil,   // arguments
		)
		if err != nil {
			t.Fatalf("Failed to declare queue: %v", err)
		}
		t.Logf("✅ Queue declared: %s", q.Name)

		// Publish a test message
		testBody := []byte(`{"test": true, "timestamp": "2026-01-01"}`)
		err = ch.Publish(
			"",     // exchange
			q.Name, // routing key
			false,  // mandatory
			false,  // immediate
			amqp.Publishing{
				ContentType: "application/json",
				Body:        testBody,
			})
		if err != nil {
			t.Fatalf("Failed to publish message: %v", err)
		}
		t.Log("✅ Test message published")

		// Consume the message back
		msgs, err := ch.Consume(
			q.Name, // queue
			"",     // consumer
			true,   // auto-ack
			false,  // exclusive
			false,  // no-local
			false,  // no-wait
			nil,    // args
		)
		if err != nil {
			t.Fatalf("Failed to consume: %v", err)
		}

		// Read one message with timeout
		select {
		case msg := <-msgs:
			t.Logf("✅ Received message: %s", string(msg.Body))
		case <-time.After(5 * time.Second):
			t.Fatal("Timeout waiting for message")
		}
	})
}

// TestEmailService_AMQP_Integration tests EmailService with real AMQP
func TestEmailService_AMQP_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test - set RUN_INTEGRATION_TESTS=true to run")
	}

	amqpURL := os.Getenv("AMQP_URL")
	dbURL := os.Getenv("DATABASE_URL")
	if amqpURL == "" || dbURL == "" {
		t.Skip("Missing AMQP_URL or DATABASE_URL in environment")
	}

	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	// First, ensure email_settings has the AMQP URL
	// ID must be a valid UUID.
	var settingsID string
	err = db.QueryRow("SELECT id FROM email_settings LIMIT 1").Scan(&settingsID)
	if err == sql.ErrNoRows {
		settingsID = "00000000-0000-0000-0000-000000000001"
		_, err = db.Exec(`
			INSERT INTO email_settings (id, amqp_url, is_active, updated_at) 
			VALUES ($1, $2, true, NOW())
		`, settingsID, amqpURL)
	} else {
		_, err = db.Exec(`UPDATE email_settings SET amqp_url = $1, is_active = true WHERE id = $2`, amqpURL, settingsID)
	}
	if err != nil {
		t.Logf("Warning: Could not update email_settings: %v", err)
	}

	credsSvc := services.NewCredentialsService(db)
	emailSvc := services.NewEmailService(db, credsSvc)
	ctx := context.Background()

	t.Run("queues email via RabbitMQ", func(t *testing.T) {
		err := emailSvc.SendTemplateEmail(ctx, "test@example.com", "welcome", map[string]interface{}{
			"name": "Integration Test",
		})
		if err != nil {
			// This might fail if email_settings doesn't have amqp_url configured correctly
			t.Logf("SendTemplateEmail error (expected if email_settings not configured): %v", err)
		} else {
			t.Log("✅ Email queued successfully via RabbitMQ")
		}
	})
}

// TestCompanyService_Integration tests admin/company management with real DB
func TestCompanyService_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test")
	}

	dbURL := os.Getenv("DATABASE_URL")
	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	adminSvc := services.NewAdminService(db) // AdminService handles companies
	ctx := context.Background()

	t.Run("lists companies", func(t *testing.T) {
		companies, total, err := adminSvc.ListCompanies(ctx, nil, 1, 10)
		if err != nil {
			t.Fatalf("ListCompanies failed: %v", err)
		}
		t.Logf("✅ Listed %d companies (total: %d)", len(companies), total)
	})
}

// TestJobService_CRUD_Integration tests full job lifecycle
func TestJobService_CRUD_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test")
	}

	dbURL := os.Getenv("DATABASE_URL")
	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	jobSvc := services.NewJobService(db)

	// Need a valid company ID. Use first one found.
	var companyID string
	err = db.QueryRow("SELECT id FROM companies LIMIT 1").Scan(&companyID)
	if err != nil {
		t.Logf("Skipping job creation test: no companies found (%v)", err)
		return
	}

	// Need a valid User ID (createdBy)
	var userID string
	err = db.QueryRow("SELECT id FROM users LIMIT 1").Scan(&userID)
	if err != nil {
		// Fallback to a dummy UUID if no users exist
		userID = "00000000-0000-0000-0000-000000000000"
	}

	t.Run("creates, updates and deletes job", func(t *testing.T) {
		// 1. Create
		req := dto.CreateJobRequest{
			CompanyID:      companyID,
			Title:          "Integration Test Job",
			Description:    "Test Description that is quite long to satisfy validation requirements",
			EmploymentType: ptrString("full-time"),
			Location:       ptrString("Remote"),
			Status:         "draft",
		}

		// Create
		job, err := jobSvc.CreateJob(req, userID)
		if err != nil {
			t.Fatalf("CreateJob failed: %v", err)
		}
		t.Logf("✅ Created job: %s", job.ID)

		// 2. Get
		fetched, err := jobSvc.GetJobByID(job.ID)
		if err != nil {
			t.Fatalf("GetJobByID failed: %v", err)
		}
		if fetched.Title != req.Title {
			t.Errorf("Title mismatch: expected %s, got %s", req.Title, fetched.Title)
		}

		// 3. Update
		newTitle := "Updated Integration Job"
		updated, err := jobSvc.UpdateJob(job.ID, dto.UpdateJobRequest{Title: &newTitle})
		if err != nil {
			t.Fatalf("UpdateJob failed: %v", err)
		}
		if updated.Title != newTitle {
			t.Errorf("Update failed: expected %s, got %s", newTitle, updated.Title)
		}

		// 4. Update Status (using UpdateJob)
		newStatus := "closed"
		_, err = jobSvc.UpdateJob(job.ID, dto.UpdateJobRequest{Status: &newStatus})
		if err != nil {
			t.Fatalf("UpdateJob (Status) failed: %v", err)
		}

		// Cleanup: Delete
		err = jobSvc.DeleteJob(job.ID)
		if err != nil {
			t.Fatalf("DeleteJob failed: %v", err)
		}
		t.Log("✅ Deleted job")
	})
}

// TestApplicationService_Integration tests application lifecycle
func TestApplicationService_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test")
	}

	dbURL := os.Getenv("DATABASE_URL")
	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	jobSvc := services.NewJobService(db)
	appSvc := services.NewApplicationService(db)

	// Setup: Need a Job
	var companyID, userID string
	err = db.QueryRow("SELECT id FROM companies LIMIT 1").Scan(&companyID)
	if err != nil {
		t.Skip("No companies found")
	}
	err = db.QueryRow("SELECT id FROM users LIMIT 1").Scan(&userID)
	if err != nil {
		userID = "00000000-0000-0000-0000-000000000000"
	}

	jobReq := dto.CreateJobRequest{
		CompanyID:      companyID,
		Title:          "App Test Job",
		Description:    "Job for application testing",
		EmploymentType: ptrString("full-time"),
		Location:       ptrString("Remote"),
		Status:         "open",
	}
	job, err := jobSvc.CreateJob(jobReq, userID)
	if err != nil {
		t.Fatalf("Setup: CreateJob failed: %v", err)
	}
	defer jobSvc.DeleteJob(job.ID)

	t.Run("creates and updates application", func(t *testing.T) {
		// 1. Create Application
		appReq := dto.CreateApplicationRequest{
			JobID:   job.ID,
			UserID:  &userID,
			Name:    ptrString("Applicant Name"),
			Email:   ptrString("applicant@test.com"),
			Message: ptrString("Hire me!"),
		}

		app, err := appSvc.CreateApplication(appReq)
		if err != nil {
			t.Fatalf("CreateApplication failed: %v", err)
		}
		t.Logf("✅ Created application: %s", app.ID)

		// 2. Update Status
		statusReq := dto.UpdateApplicationStatusRequest{
			Status: "reviewed",
			Notes:  ptrString("Looks good"),
		}
		updated, err := appSvc.UpdateApplicationStatus(app.ID, statusReq)
		if err != nil {
			t.Fatalf("UpdateApplicationStatus failed: %v", err)
		}
		if updated.Status != "reviewed" {
			t.Errorf("Status mismatch: expected reviewed, got %s", updated.Status)
		}
		t.Log("✅ Updated application status")

		// Cleanup: Delete Application
		err = appSvc.DeleteApplication(app.ID)
		if err != nil {
			t.Fatalf("DeleteApplication failed: %v", err)
		}
	})
}

// TestAdminService_Integration tests admin actions
func TestAdminService_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test")
	}

	dbURL := os.Getenv("DATABASE_URL")
	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	adminSvc := services.NewAdminService(db)

	// Need a company to modify
	var companyID string
	err = db.QueryRow("SELECT id FROM companies LIMIT 1").Scan(&companyID)
	if err != nil {
		t.Skip("No companies found")
	}

	t.Run("updates company verification status", func(t *testing.T) {
		verified := true
		active := true
		req := dto.UpdateCompanyRequest{
			Verified: &verified,
			Active:   &active,
		}

		updated, err := adminSvc.UpdateCompany(context.Background(), companyID, req)
		if err != nil {
			t.Fatalf("UpdateCompany failed: %v", err)
		}
		if !updated.Verified {
			t.Errorf("Company should be verifying")
		}
		t.Logf("✅ Company verification updated")
	})

	// Test GetEmailSettings and others
	t.Run("get email settings", func(t *testing.T) {
		settings, err := adminSvc.GetEmailSettings(context.Background())
		if err != nil {
			t.Fatalf("GetEmailSettings failed: %v", err)
		}
		if settings != nil {
			t.Logf("✅ Got email settings: %s", settings.ID)
		}
	})
}

// TestJobService_Filters_Integration tests complex job queries
func TestJobService_Filters_Integration(t *testing.T) {
	if os.Getenv("RUN_INTEGRATION_TESTS") != "true" {
		t.Skip("Skipping integration test")
	}

	dbURL := os.Getenv("DATABASE_URL")
	db, err := sql.Open("postgres", dbURL)
	if err != nil {
		t.Fatalf("Failed to open database: %v", err)
	}
	defer db.Close()

	jobSvc := services.NewJobService(db)

	t.Run("filters jobs by multiple criteria", func(t *testing.T) {
		mode := "remote"
		salaryMin := 1000.0
		filter := dto.JobFilterQuery{
			WorkMode:  &mode,
			SalaryMin: &salaryMin,
			SortBy:    ptrString("recent"),
		}
		filter.Limit = 5

		jobs, _, err := jobSvc.GetJobs(filter)
		if err != nil {
			t.Fatalf("GetJobs with filter failed: %v", err)
		}
		t.Logf("✅ Listed %d jobs with filters", len(jobs))
	})
}
