//go:build e2e
// +build e2e

package e2e

import (
	"fmt"
	"net/http"
	"testing"
	"time"

	"github.com/rede5/gohorsejobs/backend/internal/database"
	"github.com/rede5/gohorsejobs/backend/internal/dto"
	"github.com/rede5/gohorsejobs/backend/internal/models"
)

// setupTestJobForApplications creates a test company, user, and job for application tests
func setupTestJobForApplications(t *testing.T) (companyID, userID, jobID string) {
	t.Helper()

	// Create user first
	userQuery := `
		INSERT INTO users (identifier, password_hash, role, full_name, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6)
		ON CONFLICT (identifier) DO UPDATE SET password_hash = $2
		RETURNING id
	`
	err := database.DB.QueryRow(
		userQuery,
		"e2e-test-apps-user",
		"hashedpassword",
		"superadmin",
		"E2E Test Apps User",
		time.Now(),
		time.Now(),
	).Scan(&userID)

	if err != nil {
		t.Fatalf("Failed to create test user: %v", err)
	}

	// Create company
	companyQuery := `
		INSERT INTO companies (name, slug, type, active, verified, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7)
		ON CONFLICT (slug) DO UPDATE SET name = $1
		RETURNING id
	`
	err = database.DB.QueryRow(
		companyQuery,
		"E2E Test Company for Apps",
		"e2e-test-company-apps",
		"employer",
		true,
		false,
		time.Now(),
		time.Now(),
	).Scan(&companyID)

	if err != nil {
		t.Fatalf("Failed to create test company: %v", err)
	}

	// Create job
	jobQuery := `
		INSERT INTO jobs (company_id, created_by, title, description, status, visa_support, created_at, updated_at)
		VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
		RETURNING id
	`
	err = database.DB.QueryRow(
		jobQuery,
		companyID,
		userID,
		"E2E Test Job for Applications",
		"Test job to receive applications",
		"open",
		false,
		time.Now(),
		time.Now(),
	).Scan(&jobID)

	if err != nil {
		t.Fatalf("Failed to create test job: %v", err)
	}

	return companyID, userID, jobID
}

// cleanupTestJobForApplications removes test data
func cleanupTestJobForApplications(t *testing.T, companyID, userID, jobID string) {
	t.Helper()
	database.DB.Exec("DELETE FROM applications WHERE job_id = $1", jobID)
	database.DB.Exec("DELETE FROM jobs WHERE id = $1", jobID)
	database.DB.Exec("DELETE FROM companies WHERE id = $1", companyID)
}

// TestE2E_Applications_CRUD tests the complete application flow
func TestE2E_Applications_CRUD(t *testing.T) {
	client := newTestClient()
	companyID, userID, jobID := setupTestJobForApplications(t)
	defer cleanupTestJobForApplications(t, companyID, userID, jobID)

	var createdAppID string

	// =====================
	// 1. CREATE APPLICATION
	// =====================
	t.Run("CreateApplication", func(t *testing.T) {
		name := "John Doe E2E Test"
		email := "john.e2e@test.com"
		phone := "+81-90-1234-5678"
		message := "I am interested in this position. This is an E2E test application."

		appReq := dto.CreateApplicationRequest{
			JobID:   jobID,
			Name:    &name,
			Email:   &email,
			Phone:   &phone,
			Message: &message,
		}

		resp, err := client.post("/api/v1/applications", appReq)
		if err != nil {
			t.Fatalf("Failed to create application: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusCreated {
			t.Errorf("Expected status 201, got %d", resp.StatusCode)
		}

		var app models.Application
		if err := parseJSON(resp, &app); err != nil {
			t.Fatalf("Failed to parse response: %v", err)
		}

		if app.Status != "pending" {
			t.Errorf("Expected status 'pending', got '%s'", app.Status)
		}

		if app.JobID != jobID {
			t.Errorf("Expected jobID %s, got %s", jobID, app.JobID)
		}

		createdAppID = app.ID
		t.Logf("Created application with ID: %s", createdAppID)
	})

	// =====================
	// 2. GET APPLICATION BY ID
	// =====================
	t.Run("GetApplicationByID", func(t *testing.T) {
		if createdAppID == "" {
			t.Skip("No application was created")
		}

		resp, err := client.get(fmt.Sprintf("/api/v1/applications/%s", createdAppID))
		if err != nil {
			t.Fatalf("Failed to get application: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		var app models.Application
		if err := parseJSON(resp, &app); err != nil {
			t.Fatalf("Failed to parse response: %v", err)
		}

		if app.ID != createdAppID {
			t.Errorf("Expected application ID %s, got %s", createdAppID, app.ID)
		}
	})

	// =====================
	// 3. LIST APPLICATIONS BY JOB
	// =====================
	t.Run("ListApplicationsByJob", func(t *testing.T) {
		resp, err := client.get(fmt.Sprintf("/api/v1/applications?jobId=%s", jobID))
		if err != nil {
			t.Fatalf("Failed to list applications: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		var apps []models.Application
		if err := parseJSON(resp, &apps); err != nil {
			t.Fatalf("Failed to parse response: %v", err)
		}

		if len(apps) < 1 {
			t.Error("Expected at least 1 application")
		}
	})

	// =====================
	// 4. UPDATE APPLICATION STATUS
	// =====================
	t.Run("UpdateStatusToReviewed", func(t *testing.T) {
		if createdAppID == "" {
			t.Skip("No application was created")
		}

		statusReq := dto.UpdateApplicationStatusRequest{
			Status: "reviewed",
		}

		resp, err := client.put(fmt.Sprintf("/api/v1/applications/%s/status", createdAppID), statusReq)
		if err != nil {
			t.Fatalf("Failed to update status: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		var app models.Application
		if err := parseJSON(resp, &app); err != nil {
			t.Fatalf("Failed to parse response: %v", err)
		}

		if app.Status != "reviewed" {
			t.Errorf("Expected status 'reviewed', got '%s'", app.Status)
		}
	})

	// =====================
	// 5. UPDATE TO HIRED
	// =====================
	t.Run("UpdateStatusToHired", func(t *testing.T) {

		if createdAppID == "" {
			t.Skip("No application was created")
		}

		statusReq := dto.UpdateApplicationStatusRequest{
			Status: "hired",
		}

		resp, err := client.put(fmt.Sprintf("/api/v1/applications/%s/status", createdAppID), statusReq)
		if err != nil {
			t.Fatalf("Failed to update status: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusOK {
			t.Errorf("Expected status 200, got %d", resp.StatusCode)
		}

		var app models.Application
		if err := parseJSON(resp, &app); err != nil {
			t.Fatalf("Failed to parse response: %v", err)
		}

		if app.Status != "hired" {
			t.Errorf("Expected status 'hired', got '%s'", app.Status)
		}
	})
}

// TestE2E_Applications_MultipleApplicants tests multiple applications for the same job
func TestE2E_Applications_MultipleApplicants(t *testing.T) {
	client := newTestClient()
	companyID, userID, jobID := setupTestJobForApplications(t)
	defer cleanupTestJobForApplications(t, companyID, userID, jobID)

	// Create 3 applications
	applicants := []string{"Alice", "Bob", "Charlie"}
	for _, name := range applicants {
		appName := name + " E2E Test"
		email := name + "@e2etest.com"
		appReq := dto.CreateApplicationRequest{
			JobID: jobID,
			Name:  &appName,
			Email: &email,
		}
		resp, _ := client.post("/api/v1/applications", appReq)
		resp.Body.Close()
	}

	// List all applications for job
	resp, err := client.get(fmt.Sprintf("/api/v1/applications?jobId=%s", jobID))
	if err != nil {
		t.Fatalf("Failed to list applications: %v", err)
	}
	defer resp.Body.Close()

	var apps []models.Application
	if err := parseJSON(resp, &apps); err != nil {
		t.Fatalf("Failed to parse response: %v", err)
	}

	if len(apps) != 3 {
		t.Errorf("Expected 3 applications, got %d", len(apps))
	}
}

// TestE2E_Applications_Errors tests error handling
func TestE2E_Applications_Errors(t *testing.T) {
	client := newTestClient()

	t.Run("GetNonExistentApplication", func(t *testing.T) {
		resp, err := client.get("/api/v1/applications/999999")
		if err != nil {
			t.Fatalf("Failed to make request: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusNotFound {
			t.Errorf("Expected status 404, got %d", resp.StatusCode)
		}
	})

	t.Run("ListApplicationsMissingJobId", func(t *testing.T) {
		resp, err := client.get("/api/v1/applications")
		if err != nil {
			t.Fatalf("Failed to make request: %v", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != http.StatusBadRequest {
			t.Errorf("Expected status 400, got %d", resp.StatusCode)
		}
	})
}
