/**
 * QA-APIKEY-020: API Keys Tests
 * 
 * E2E Test Suite for API Key Management
 * - Create API Key
 * - Revoke API Key
 * - API Access with Key
 * - Key Rotation
 */

import { test, expect } from '@playwright/test';
import { navigateTo, waitForLoading, generateTestScenarioName } from './utils/test-helpers';
import {
  generateTestUser,
  loginUserViaUI,
  registerUserViaAPI,
  createApiKeyViaAPI,
  listApiKeys,
  revokeApiKey,
  createAuthHeader,
  createApiKeyHeader,
} from './utils/auth-helpers';

// Store test data for cleanup
let testUser: { email: string; password: string; fullName: string } | null = null;
let accessToken: string | null = null;
let apiKey: string | null = null;
let apiKeyId: string | null = null;

// ============================================
// TEST SUITE: API Key Creation (UI)
// ============================================
test.describe('QA-APIKEY-020: Create API Key - UI', () => {
  test.beforeEach(async ({ page, request }) => {
    // Register and login user
    testUser = generateTestUser('APIKey');
    const auth = await registerUserViaAPI(
      request,
      testUser.email,
      testUser.password,
      testUser.fullName
    );
    accessToken = auth.access_token;

    // Login via UI
    await loginUserViaUI(page, testUser.email, testUser.password);
  });

  test('should navigate to API Keys settings page', async ({ page }) => {
    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Verify page loaded
    await expect(page.getByRole('heading', { name: /api keys|api keys management/i })).toBeVisible();
  });

  test('should create API key and display modal with full key', async ({ page }) => {
    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Click create new key button
    await page.getByRole('button', { name: /create|generate|new.*key/i }).click();

    // Fill form
    await page.getByLabel(/name|key name/i).fill('Test API Key');
    
    // Select scopes if available
    const scopeCheckboxes = page.locator('input[type="checkbox"][name*="scope"], [data-testid*="scope"]');
    if (await scopeCheckboxes.first().isVisible().catch(() => false)) {
      await scopeCheckboxes.first().check();
    }

    // Submit form
    await page.getByRole('button', { name: /create|generate|save/i }).click();

    // Verify modal appears with the full key
    const modal = page.locator('[role="dialog"], [data-testid="api-key-modal"], .modal').first();
    await expect(modal).toBeVisible({ timeout: 5000 });

    // Verify key is displayed
    await expect(modal.getByText(/mk_/i).or(modal.locator('input[value*="mk_"]'))).toBeVisible();

    // Verify warning message
    await expect(
      modal.getByText(/copy now|only see once|save.*key|cannot.*see.*again/i).first()
    ).toBeVisible();
  });

  test('should copy API key to clipboard', async ({ page, context }) => {
    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Create a key
    await page.getByRole('button', { name: /create|generate|new.*key/i }).click();
    await page.getByLabel(/name|key name/i).fill('Clipboard Test Key');
    await page.getByRole('button', { name: /create|generate|save/i }).click();

    // Wait for modal
    const modal = page.locator('[role="dialog"], [data-testid="api-key-modal"], .modal').first();
    await expect(modal).toBeVisible({ timeout: 5000 });

    // Click copy button
    const copyButton = modal.getByRole('button', { name: /copy|clipboard/i });
    if (await copyButton.isVisible().catch(() => false)) {
      await copyButton.click();
      
      // Verify copy success message or toast
      await expect(
        page.getByText(/copied|clipboard|success/i).first()
      ).toBeVisible({ timeout: 3000 });
    }
  });

  test('should show API key in list after creation', async ({ page }) => {
    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Create a key
    const keyName = 'List Test Key';
    await page.getByRole('button', { name: /create|generate|new.*key/i }).click();
    await page.getByLabel(/name|key name/i).fill(keyName);
    await page.getByRole('button', { name: /create|generate|save/i }).click();

    // Close modal if present
    const modal = page.locator('[role="dialog"], [data-testid="api-key-modal"], .modal').first();
    if (await modal.isVisible().catch(() => false)) {
      const closeButton = modal.getByRole('button', { name: /close|done|ok/i });
      await closeButton.click();
    }

    // Refresh page
    await page.reload();
    await page.waitForLoadState('networkidle');

    // Verify key appears in list
    await expect(page.getByText(keyName)).toBeVisible();
  });

  test('should validate required fields when creating API key', async ({ page }) => {
    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Click create new key button
    await page.getByRole('button', { name: /create|generate|new.*key/i }).click();

    // Submit without filling name
    await page.getByRole('button', { name: /create|generate|save/i }).click();

    // Verify validation error
    await expect(
      page.getByText(/required|name.*required|please enter/i).first()
    ).toBeVisible({ timeout: 5000 });
  });
});

// ============================================
// TEST SUITE: API Key Revocation (UI)
// ============================================
test.describe('QA-APIKEY-020: Revoke API Key - UI', () => {
  test.beforeEach(async ({ page, request }) => {
    // Register and login user
    testUser = generateTestUser('RevokeKey');
    const auth = await registerUserViaAPI(
      request,
      testUser.email,
      testUser.password,
      testUser.fullName
    );
    accessToken = auth.access_token;

    // Login via UI
    await loginUserViaUI(page, testUser.email, testUser.password);
  });

  test('should revoke API key and remove from list', async ({ page, request }) => {
    // Create an API key via API first
    const newKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Key To Revoke',
      ['read:scenarios']
    );

    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Find the key in list
    await expect(page.getByText('Key To Revoke')).toBeVisible();

    // Click revoke/delete button
    const revokeButton = page.locator('tr', { hasText: 'Key To Revoke' }).getByRole('button', { name: /revoke|delete|remove/i });
    await revokeButton.click();

    // Confirm revocation if confirmation dialog appears
    const confirmButton = page.getByRole('button', { name: /confirm|yes|revoke/i });
    if (await confirmButton.isVisible().catch(() => false)) {
      await confirmButton.click();
    }

    // Verify key is no longer in list
    await page.reload();
    await page.waitForLoadState('networkidle');
    
    await expect(page.getByText('Key To Revoke')).not.toBeVisible();
  });

  test('should show confirmation before revoking', async ({ page, request }) => {
    // Create an API key via API
    const newKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Key With Confirmation',
      ['read:scenarios']
    );

    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Find and click revoke
    const revokeButton = page.locator('tr', { hasText: 'Key With Confirmation' }).getByRole('button', { name: /revoke|delete/i });
    await revokeButton.click();

    // Verify confirmation dialog
    await expect(
      page.getByText(/are you sure|confirm.*revoke|cannot.*undo/i).first()
    ).toBeVisible({ timeout: 5000 });
  });
});

// ============================================
// TEST SUITE: API Access with Key (API)
// ============================================
test.describe('QA-APIKEY-020: API Access with Key', () => {
  test.beforeAll(async ({ request }) => {
    // Register test user
    testUser = generateTestUser('APIAccess');
    const auth = await registerUserViaAPI(
      request,
      testUser.email,
      testUser.password,
      testUser.fullName
    );
    accessToken = auth.access_token;
  });

  test('should access API with valid API key header', async ({ request }) => {
    // Create an API key
    const newKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Valid Access Key',
      ['read:scenarios']
    );
    apiKey = newKey.key;
    apiKeyId = newKey.id;

    // Make API request with API key
    const response = await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader(apiKey),
    });

    // Should be authorized
    expect(response.status()).not.toBe(401);
    expect(response.status()).not.toBe(403);
  });

  test('should access /auth/me with valid API key', async ({ request }) => {
    // Create an API key
    const newKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Me Endpoint Key',
      ['read:scenarios']
    );

    // Make API request
    const response = await request.get('http://localhost:8000/api/v1/auth/me', {
      headers: createApiKeyHeader(newKey.key),
    });

    expect(response.ok()).toBeTruthy();
    
    const data = await response.json();
    expect(data).toHaveProperty('id');
    expect(data).toHaveProperty('email');
  });

  test('should return 401 with revoked API key', async ({ request }) => {
    // Create an API key
    const newKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Key To Revoke For Test',
      ['read:scenarios']
    );

    // Revoke the key
    await revokeApiKey(request, accessToken!, newKey.id);

    // Try to use revoked key
    const response = await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader(newKey.key),
    });

    expect(response.status()).toBe(401);
  });

  test('should return 401 with invalid API key format', async ({ request }) => {
    const response = await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader('invalid_key_format'),
    });

    expect(response.status()).toBe(401);
  });

  test('should return 401 with non-existent API key', async ({ request }) => {
    const response = await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader('mk_nonexistentkey12345678901234'),
    });

    expect(response.status()).toBe(401);
  });

  test('should return 401 without API key header', async ({ request }) => {
    const response = await request.get('http://localhost:8000/api/v1/scenarios');

    // Should require authentication
    expect(response.status()).toBe(401);
  });

  test('should respect API key scopes', async ({ request }) => {
    // Create a read-only API key
    const readKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Read Only Key',
      ['read:scenarios']
    );

    // Read should work
    const readResponse = await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader(readKey.key),
    });

    // Should be allowed for read operations
    expect(readResponse.status()).not.toBe(403);
  });

  test('should track API key last used timestamp', async ({ request }) => {
    // Create an API key
    const newKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Track Usage Key',
      ['read:scenarios']
    );

    // Use the key
    await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader(newKey.key),
    });

    // Check if last_used is updated (API dependent)
    const listResponse = await request.get('http://localhost:8000/api/v1/api-keys', {
      headers: createAuthHeader(accessToken!),
    });

    if (listResponse.ok()) {
      const keys = await listResponse.json();
      const key = keys.find((k: { id: string }) => k.id === newKey.id);
      if (key && key.last_used_at) {
        expect(key.last_used_at).toBeTruthy();
      }
    }
  });
});

// ============================================
// TEST SUITE: API Key Management (API)
// ============================================
test.describe('QA-APIKEY-020: API Key Management - API', () => {
  test.beforeAll(async ({ request }) => {
    // Register test user
    testUser = generateTestUser('KeyMgmt');
    const auth = await registerUserViaAPI(
      request,
      testUser.email,
      testUser.password,
      testUser.fullName
    );
    accessToken = auth.access_token;
  });

  test('should list all API keys for user', async ({ request }) => {
    // Create a couple of keys
    await createApiKeyViaAPI(request, accessToken!, 'Key 1', ['read:scenarios']);
    await createApiKeyViaAPI(request, accessToken!, 'Key 2', ['read:scenarios', 'write:scenarios']);

    // List keys
    const keys = await listApiKeys(request, accessToken!);

    expect(keys.length).toBeGreaterThanOrEqual(2);
    expect(keys.some(k => k.name === 'Key 1')).toBe(true);
    expect(keys.some(k => k.name === 'Key 2')).toBe(true);
  });

  test('should not expose full API key in list response', async ({ request }) => {
    // Create a key
    const newKey = await createApiKeyViaAPI(request, accessToken!, 'Hidden Key', ['read:scenarios']);

    // List keys
    const keys = await listApiKeys(request, accessToken!);

    const key = keys.find(k => k.id === newKey.id);
    expect(key).toBeDefined();
    
    // Should have prefix but not full key
    expect(key).toHaveProperty('prefix');
    expect(key).not.toHaveProperty('key');
    expect(key).not.toHaveProperty('key_hash');
  });

  test('should create API key with expiration', async ({ request }) => {
    // Create key with 7 day expiration
    const newKey = await createApiKeyViaAPI(
      request,
      accessToken!,
      'Expiring Key',
      ['read:scenarios'],
      7
    );

    expect(newKey).toHaveProperty('id');
    expect(newKey).toHaveProperty('key');
    expect(newKey.key).toMatch(/^mk_/);
  });

  test('should rotate API key', async ({ request }) => {
    // Create a key
    const oldKey = await createApiKeyViaAPI(request, accessToken!, 'Rotatable Key', ['read:scenarios']);

    // Rotate the key
    const rotateResponse = await request.post(
      `http://localhost:8000/api/v1/api-keys/${oldKey.id}/rotate`,
      { headers: createAuthHeader(accessToken!) }
    );

    if (rotateResponse.status() === 404) {
      test.skip(true, 'Key rotation endpoint not implemented');
    }

    expect(rotateResponse.ok()).toBeTruthy();
    
    const newKeyData = await rotateResponse.json();
    expect(newKeyData).toHaveProperty('key');
    expect(newKeyData.key).not.toBe(oldKey.key);

    // Old key should no longer work
    const oldKeyResponse = await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader(oldKey.key),
    });
    expect(oldKeyResponse.status()).toBe(401);

    // New key should work
    const newKeyResponse = await request.get('http://localhost:8000/api/v1/scenarios', {
      headers: createApiKeyHeader(newKeyData.key),
    });
    expect(newKeyResponse.ok()).toBeTruthy();
  });
});

// ============================================
// TEST SUITE: API Key UI - List View
// ============================================
test.describe('QA-APIKEY-020: API Key List View', () => {
  test.beforeEach(async ({ page, request }) => {
    // Register and login user
    testUser = generateTestUser('ListView');
    const auth = await registerUserViaAPI(
      request,
      testUser.email,
      testUser.password,
      testUser.fullName
    );
    accessToken = auth.access_token;

    // Login via UI
    await loginUserViaUI(page, testUser.email, testUser.password);
  });

  test('should display API keys table with correct columns', async ({ page }) => {
    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Verify table headers
    await expect(page.getByRole('columnheader', { name: /name/i })).toBeVisible();
    await expect(page.getByRole('columnheader', { name: /prefix|key/i })).toBeVisible();
    await expect(page.getByRole('columnheader', { name: /scopes|permissions/i })).toBeVisible();
    await expect(page.getByRole('columnheader', { name: /created|date/i })).toBeVisible();
    await expect(page.getByRole('columnheader', { name: /actions/i })).toBeVisible();
  });

  test('should show empty state when no API keys', async ({ page }) => {
    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Verify empty state message
    await expect(
      page.getByText(/no.*keys|no.*api.*keys|get started|create.*key/i).first()
    ).toBeVisible();
  });

  test('should display key prefix for identification', async ({ page, request }) => {
    // Create a key via API
    const newKey = await createApiKeyViaAPI(request, accessToken!, 'Prefix Test Key', ['read:scenarios']);

    // Navigate to API Keys page
    await page.goto('/settings/api-keys');
    await page.waitForLoadState('networkidle');

    // Verify prefix is displayed
    await expect(page.getByText(newKey.prefix)).toBeVisible();
  });
});
