//go:build test

package user

// Package user contains tests for guest user creation endpoints.
//
// Test Coverage:
//   - CreateGuestUserHandler: POST /api/user/guest - Create a new guest user
//
// Edge case tests for validation:
//   - Invalid phone number format
//   - Empty first name
//   - Name exceeds 50 character limit
//   - Invalid email format

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"crussell/testutils"
	"crussell/testutils/fixtures"
)

// TestGuestUser_Create_InvalidPhone verifies that an invalid phone number returns 400 Bad Request.
func TestGuestUser_Create_InvalidPhone(t *testing.T) {
	t.Parallel()

	reqBody := CreateGuestUserRequest{
		FirstName: "Test",
		LastName:  "User",
		Email:     "test@test.com",
		Phone:     "not-a-phone",
	}
	body, _ := json.Marshal(reqBody)

	req := httptest.NewRequest(http.MethodPost, "/api/user/guest", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	rr := httptest.NewRecorder()
	CreateGuestUserHandler(rr, req)

	if rr.Code != http.StatusBadRequest {
		t.Errorf("expected status 400, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}
}

// TestGuestUser_Create_EmptyFirstName verifies that an empty first name returns 400 Bad Request.
func TestGuestUser_Create_EmptyFirstName(t *testing.T) {
	t.Parallel()

	reqBody := CreateGuestUserRequest{
		FirstName: "",
		LastName:  "User",
		Email:     "test@test.com",
		Phone:     "07123456789",
	}
	body, _ := json.Marshal(reqBody)

	req := httptest.NewRequest(http.MethodPost, "/api/user/guest", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	rr := httptest.NewRecorder()
	CreateGuestUserHandler(rr, req)

	if rr.Code != http.StatusBadRequest {
		t.Errorf("expected status 400, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}
}

// TestGuestUser_Create_NameTooLong verifies that a first name exceeding 50 characters returns 400 Bad Request.
func TestGuestUser_Create_NameTooLong(t *testing.T) {
	t.Parallel()

	reqBody := CreateGuestUserRequest{
		FirstName: strings.Repeat("a", 51),
		LastName:  "User",
		Email:     "test@test.com",
		Phone:     "07123456789",
	}
	body, _ := json.Marshal(reqBody)

	req := httptest.NewRequest(http.MethodPost, "/api/user/guest", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	rr := httptest.NewRecorder()
	CreateGuestUserHandler(rr, req)

	if rr.Code != http.StatusBadRequest {
		t.Errorf("expected status 400, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}
}

// TestGuestUser_Create_InvalidEmail verifies that an invalid email format returns 400 Bad Request.
func TestGuestUser_Create_InvalidEmail(t *testing.T) {
	t.Parallel()

	reqBody := CreateGuestUserRequest{
		FirstName: "Test",
		LastName:  "User",
		Email:     "not-an-email",
		Phone:     "07123456789",
	}
	body, _ := json.Marshal(reqBody)

	req := httptest.NewRequest(http.MethodPost, "/api/user/guest", bytes.NewReader(body))
	req.Header.Set("Content-Type", "application/json")

	rr := httptest.NewRecorder()
	CreateGuestUserHandler(rr, req)

	if rr.Code != http.StatusBadRequest {
		t.Errorf("expected status 400, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}
}

// TestCheckEmail_NotRegistered verifies that querying a non-existent email returns suggestion null.
func TestCheckEmail_NotRegistered(t *testing.T) {
	t.Parallel()

	req := httptest.NewRequest(http.MethodGet, "/api/check-email?email=nobody@example.com&firstName=Jane&lastName=Doe&phone=%2B447123456789", nil)
	rr := httptest.NewRecorder()
	CheckEmailHandler(rr, req)

	if rr.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}

	var resp map[string]interface{}
	if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
		t.Fatalf("failed to unmarshal response: %v", err)
	}

	if resp["suggestion"] != nil {
		t.Errorf("expected suggestion null, got %v", resp["suggestion"])
	}
}

// TestCheckEmail_Registered_MatchingDetails verifies that querying an existing registered user's email
// with matching first name, last name, and phone returns suggestion "login".
func TestCheckEmail_Registered_MatchingDetails(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUserWithEmail(tx, "jane@example.com", "verified_email")
	if err != nil {
		t.Fatalf("failed to create test user: %v", err)
	}

	_, err = tx.Exec(ctx, `
		UPDATE users SET n_first_name = 'Jane', n_last_name = 'Doe' WHERE id = $1
	`, userID)
	if err != nil {
		t.Fatalf("failed to update user name: %v", err)
	}

	req := httptest.NewRequest(http.MethodGet, `/api/check-email?email=jane@example.com&firstName=Jane&lastName=Doe&phone=%2B447123456789`, nil)
	req = req.WithContext(ctx)
	rr := httptest.NewRecorder()
	CheckEmailHandler(rr, req)

	if rr.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}

	var resp map[string]interface{}
	if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
		t.Fatalf("failed to unmarshal response: %v", err)
	}

	suggestion, ok := resp["suggestion"].(string)
	if !ok || suggestion != "login" {
		t.Errorf("expected suggestion 'login', got %v", resp["suggestion"])
	}
}

// TestCheckEmail_Registered_PartialMatch verifies that when the email exists but details don't fully match,
// the handler returns suggestion "check".
func TestCheckEmail_Registered_PartialMatch(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	userID, err := fixtures.CreateTestUserWithEmail(tx, "jane@example.com", "verified_email")
	if err != nil {
		t.Fatalf("failed to create test user: %v", err)
	}

	_, err = tx.Exec(ctx, `
		UPDATE users SET n_first_name = 'Jane', n_last_name = 'Doe' WHERE id = $1
	`, userID)
	if err != nil {
		t.Fatalf("failed to update user name: %v", err)
	}

	req := httptest.NewRequest(http.MethodGet, `/api/check-email?email=jane@example.com&firstName=Wrong&lastName=Doe&phone=%2B447123456789`, nil)
	req = req.WithContext(ctx)
	rr := httptest.NewRecorder()
	CheckEmailHandler(rr, req)

	if rr.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}

	var resp map[string]interface{}
	if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
		t.Fatalf("failed to unmarshal response: %v", err)
	}

	suggestion, ok := resp["suggestion"].(string)
	if !ok || suggestion != "check" {
		t.Errorf("expected suggestion 'check', got %v", resp["suggestion"])
	}
}

// TestCheckEmail_GuestUser verifies that a guest user's email is treated as not found
// (suggestion null) because the query excludes account_role = 'guest'.
func TestCheckEmail_GuestUser(t *testing.T) {
	t.Parallel()
	ctx, tx := testutils.SetupTestTx(t)

	_, err := fixtures.CreateTestGuestUser(tx)
	if err != nil {
		t.Fatalf("failed to create guest user: %v", err)
	}

	req := httptest.NewRequest(http.MethodGet, `/api/check-email?email=guest@test.com&firstName=Guest&lastName=User&phone=%2B447123456789`, nil)
	req = req.WithContext(ctx)
	rr := httptest.NewRecorder()
	CheckEmailHandler(rr, req)

	if rr.Code != http.StatusOK {
		t.Errorf("expected status 200, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}

	var resp map[string]interface{}
	if err := json.Unmarshal(rr.Body.Bytes(), &resp); err != nil {
		t.Fatalf("failed to unmarshal response: %v", err)
	}

	if resp["suggestion"] != nil {
		t.Errorf("expected suggestion null for guest user, got %v", resp["suggestion"])
	}
}

// TestCheckEmail_InvalidEmail verifies that an invalid email format returns 400 Bad Request.
func TestCheckEmail_InvalidEmail(t *testing.T) {
	t.Parallel()

	req := httptest.NewRequest(http.MethodGet, "/api/check-email?email=not-an-email", nil)
	rr := httptest.NewRecorder()
	CheckEmailHandler(rr, req)

	if rr.Code != http.StatusBadRequest {
		t.Errorf("expected status 400, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}
}

// TestCheckEmail_MissingEmail verifies that omitting the email query parameter returns 400 Bad Request
// with the appropriate error message.
func TestCheckEmail_MissingEmail(t *testing.T) {
	t.Parallel()

	req := httptest.NewRequest(http.MethodGet, "/api/check-email", nil)
	rr := httptest.NewRecorder()
	CheckEmailHandler(rr, req)

	if rr.Code != http.StatusBadRequest {
		t.Errorf("expected status 400, got %d", rr.Code)
		t.Logf("response body: %s", rr.Body.String())
	}

	if !strings.Contains(rr.Body.String(), "email query parameter required") {
		t.Errorf("expected 'email query parameter required' in body, got %s", rr.Body.String())
	}
}
