// src/tests/e2e/user-journey.e2e.test.ts
import { describe, it, expect, afterAll } from 'vitest';
import * as apiClient from '../../services/apiClient';
import { cleanupDb } from '../utils/cleanup';
import { poll } from '../utils/poll';

/**
 * @vitest-environment node
 */

describe('E2E User Journey', () => {
  // Use a unique email for every run to avoid collisions
  const uniqueId = Date.now();
  const userEmail = `e2e-test-${uniqueId}@example.com`;
  const userPassword = 'StrongPassword123!';

  let authToken: string;
  let userId: string | null = null;
  let shoppingListId: number;

  afterAll(async () => {
    // Safety cleanup: Ensure the user is deleted from the DB if the test fails mid-way.
    // If the test succeeds, the user deletes their own account, so this acts as a fallback.
    await cleanupDb({
      userIds: [userId],
    });
  });

  it('should complete a full user lifecycle: Register -> Login -> Manage List -> Delete Account', async () => {
    // 1. Register a new user
    const registerResponse = await apiClient.registerUser(userEmail, userPassword, 'E2E Traveler');

    expect(registerResponse.status).toBe(201);
    const registerResponseBody = await registerResponse.json();
    expect(registerResponseBody.data.message).toBe('User registered successfully!');

    // 2. Login to get the access token.
    // We poll here because even between two API calls (register and login),
    // there can be a small delay before the newly created user record is visible
    // to the transaction started by the login request. This prevents flaky test failures.
    const { response: loginResponse, responseBody: loginResponseBody } = await poll(
      async () => {
        const response = await apiClient.loginUser(userEmail, userPassword, false);
        const responseBody = response.ok ? await response.clone().json() : {};
        return { response, responseBody };
      },
      (result) => result.response.ok,
      { timeout: 10000, interval: 1000, description: 'user login after registration' },
    );

    expect(loginResponse.status).toBe(200);
    authToken = loginResponseBody.data.token;
    userId = loginResponseBody.data.userprofile.user.user_id;

    expect(authToken).toBeDefined();
    expect(userId).toBeDefined();

    // 3. Create a Shopping List
    const createListResponse = await apiClient.createShoppingList('E2E Party List', authToken);

    expect(createListResponse.status).toBe(201);
    const createListResponseBody = await createListResponse.json();
    shoppingListId = createListResponseBody.data.shopping_list_id;
    expect(shoppingListId).toBeDefined();

    // 4. Add an item to the list
    const addItemResponse = await apiClient.addShoppingListItem(
      shoppingListId,
      { customItemName: 'Chips' },
      authToken,
    );

    expect(addItemResponse.status).toBe(201);
    const addItemResponseBody = await addItemResponse.json();
    expect(addItemResponseBody.data.custom_item_name).toBe('Chips');

    // 5. Verify the list and item exist via GET
    const getListsResponse = await apiClient.fetchShoppingLists(authToken);

    expect(getListsResponse.status).toBe(200);
    const getListsResponseBody = await getListsResponse.json();
    const myLists = getListsResponseBody.data;
    const targetList = myLists.find((l: any) => l.shopping_list_id === shoppingListId);

    expect(targetList).toBeDefined();
    expect(targetList.items).toHaveLength(1);
    expect(targetList.items[0].custom_item_name).toBe('Chips');

    // 6. Delete the User Account (Self-Service)
    const deleteAccountResponse = await apiClient.deleteUserAccount(userPassword, {
      tokenOverride: authToken,
    });

    expect(deleteAccountResponse.status).toBe(200);
    const deleteResponseBody = await deleteAccountResponse.json();
    expect(deleteResponseBody.data.message).toBe('Account deleted successfully.');

    // 7. Verify Login is no longer possible
    const failLoginResponse = await apiClient.loginUser(userEmail, userPassword, false);

    expect(failLoginResponse.status).toBe(401);

    // Mark userId as null so afterAll doesn't attempt to delete it again
    userId = null;
  });
});
