import { test, expect } from '@playwright/test';
import {
  loginSeedUser,
  takeScreenshot,
  setupConsoleErrorTracking,
  setupNetworkTracking,
  assertCleanPage,
} from './utils/test-helpers';

test.describe('Journey 4: API Keys Management', () => {
  let consoleErrors: string[];
  let network: ReturnType<typeof setupNetworkTracking>;

  test.beforeEach(async ({ page }) => {
    consoleErrors = setupConsoleErrorTracking(page);
    network = setupNetworkTracking(page);
  });

  test('should load API keys page without errors', async ({ page }) => {
    await loginSeedUser(page);
    await page.goto('/dashboard/keys');
    await page.waitForTimeout(2000);
    await takeScreenshot(page, '14-api-keys-page');

    expect(page.url()).toContain('/dashboard/keys');
    assertCleanPage(consoleErrors, network);
  });

  test('should display API keys UI with create button', async ({ page }) => {
    await loginSeedUser(page);
    await page.goto('/dashboard/keys');
    await page.waitForTimeout(2000);

    await takeScreenshot(page, '15-api-keys-ui');

    const pageContent = await page.textContent('body');
    expect(pageContent).toBeTruthy();
    assertCleanPage(consoleErrors, network);
  });

  test('should create and revoke an API key via API', async ({ page }) => {
    await loginSeedUser(page);

    // Create a key
    const createResponse = await page.request.post('/api/keys', {
      data: { name: `E2E-test-key-${Date.now()}` },
    });
    expect(createResponse.status()).toBe(201);
    const keyData = await createResponse.json();
    expect(keyData.id).toBeTruthy();
    expect(keyData.key).toMatch(/^lh_sk_/);
    expect(keyData.prefix).toBeTruthy();

    // Verify it appears in the list
    const listResponse = await page.request.get('/api/keys');
    expect(listResponse.status()).toBe(200);
    const { keys } = await listResponse.json();
    const found = keys.find((k: { id: string }) => k.id === keyData.id);
    expect(found).toBeTruthy();
    expect(found.name).toContain('E2E-test-key');

    // Revoke the key
    const revokeResponse = await page.request.delete(`/api/keys/${keyData.id}`);
    expect(revokeResponse.status()).toBe(200);

    // Verify it's gone from active keys
    const listAfterRevoke = await page.request.get('/api/keys');
    const { keys: keysAfterRevoke } = await listAfterRevoke.json();
    const stillFound = keysAfterRevoke.find((k: { id: string }) => k.id === keyData.id);
    expect(stillFound).toBeUndefined();

    assertCleanPage(consoleErrors, network);
  });

  test('should enforce free plan key limit of 2', async ({ page }) => {
    await loginSeedUser(page);

    // First, clean up any existing test keys
    const existingResponse = await page.request.get('/api/keys');
    const { keys: existingKeys } = await existingResponse.json();
    for (const key of existingKeys) {
      if (key.name.includes('E2E-limit-test')) {
        await page.request.delete(`/api/keys/${key.id}`);
      }
    }

    // Get the current count of active keys
    const currentResponse = await page.request.get('/api/keys');
    const { keys: currentKeys } = await currentResponse.json();
    const existingCount = currentKeys.length;

    // Create keys up to the limit (2 for free plan)
    const createdKeyIds: string[] = [];
    const keysToCreate = Math.min(2 - existingCount, 2);

    for (let i = 0; i < keysToCreate; i++) {
      const res = await page.request.post('/api/keys', {
        data: { name: `E2E-limit-test-${i}-${Date.now()}` },
      });
      if (res.status() === 201) {
        const data = await res.json();
        createdKeyIds.push(data.id);
      }
    }

    // Now try to create one more — should fail with 403
    const overLimitResponse = await page.request.post('/api/keys', {
      data: { name: `E2E-limit-test-over-${Date.now()}` },
    });
    expect(overLimitResponse.status()).toBe(403);
    const errorData = await overLimitResponse.json();
    expect(errorData.error).toContain('limit reached');

    await takeScreenshot(page, '15a-api-key-limit-reached');

    // Cleanup: revoke test keys
    for (const id of createdKeyIds) {
      await page.request.delete(`/api/keys/${id}`);
    }

    assertCleanPage(consoleErrors, network);
  });

  test('should reject unauthenticated API key access', async ({ page }) => {
    const response = await page.request.get('/api/keys');
    expect(response.status()).toBe(401);
  });

  test('should reject key creation without name', async ({ page }) => {
    await loginSeedUser(page);

    const response = await page.request.post('/api/keys', {
      data: { name: '' },
    });
    expect(response.status()).toBe(400);

    const response2 = await page.request.post('/api/keys', {
      data: {},
    });
    expect(response2.status()).toBe(400);
  });

  test('should reject key name longer than 64 characters', async ({ page }) => {
    await loginSeedUser(page);

    const longName = 'a'.repeat(65);
    const response = await page.request.post('/api/keys', {
      data: { name: longName },
    });
    expect(response.status()).toBe(400);
    const data = await response.json();
    expect(data.error).toContain('64 characters');
  });

  test('should return 404 when revoking non-existent key', async ({ page }) => {
    await loginSeedUser(page);

    const response = await page.request.delete('/api/keys/nonexistent-id-12345');
    expect(response.status()).toBe(404);
  });

  test('should create API key via dashboard UI and see it listed', async ({ page }) => {
    await loginSeedUser(page);
    await page.goto('/dashboard/keys');
    await page.waitForTimeout(2000);

    const keyName = `E2E-ui-key-${Date.now()}`;

    // Fill the key name input and click Create Key
    await page.fill('input[placeholder*="Key name"]', keyName);
    await page.click('button:has-text("Create Key")');
    await page.waitForTimeout(2000);
    await takeScreenshot(page, '16-api-key-created-ui');

    // The new key banner should appear with the full key value
    const banner = page.locator('text=API key created');
    await expect(banner).toBeVisible({ timeout: 5000 });

    // The full key should be displayed (starts with lh_sk_)
    const keyCode = page.locator('code').filter({ hasText: /^lh_sk_/ });
    await expect(keyCode).toBeVisible();

    // Capture the key value for Bearer auth test
    const fullKey = await keyCode.textContent();
    expect(fullKey).toMatch(/^lh_sk_/);

    // Dismiss the banner
    await page.click('button:has-text("Dismiss")');
    await page.waitForTimeout(500);

    // Verify the key appears in the table
    const pageContent = await page.textContent('body');
    expect(pageContent).toContain(keyName);
    await takeScreenshot(page, '17-api-key-listed');

    // Cleanup: revoke via API
    const listResponse = await page.request.get('/api/keys');
    const { keys } = await listResponse.json();
    const created = keys.find((k: { name: string }) => k.name === keyName);
    if (created) {
      await page.request.delete(`/api/keys/${created.id}`);
    }

    assertCleanPage(consoleErrors, network);
  });

  test('should use created API key as Bearer token for proxy request', async ({ page }) => {
    await loginSeedUser(page);

    // Create a key via API
    const keyName = `E2E-bearer-test-${Date.now()}`;
    const createResponse = await page.request.post('/api/keys', {
      data: { name: keyName },
    });
    expect(createResponse.status()).toBe(201);
    const keyData = await createResponse.json();
    const apiKey = keyData.key;
    expect(apiKey).toMatch(/^lh_sk_/);

    // Use the key as Bearer token to hit the proxy /v1/models endpoint
    // This validates the key is accepted for proxy authentication
    const proxyResponse = await page.request.get('/v1/models', {
      headers: { Authorization: `Bearer ${apiKey}` },
    });

    // The proxy may return 200 (models list) or 502/503 if no backend is running,
    // but it should NOT return 401/403 — that would mean the key was rejected.
    const status = proxyResponse.status();
    expect(status).not.toBe(401);
    expect(status).not.toBe(403);

    await takeScreenshot(page, '18-bearer-auth-proxy');

    // Cleanup: revoke the key
    await page.request.delete(`/api/keys/${keyData.id}`);

    assertCleanPage(consoleErrors, network);
  });
});
