/**
 * E2E Test Generator
 * Generates end-to-end tests using Playwright
 */

import { TestGenerator, GeneratorContext, GenerationResult, GeneratedFile } from '../types';
import * as path from 'path';

export class E2ETestGenerator implements TestGenerator {
  id = 'e2e-tests';
  name = 'E2E Test Generator';
  description = 'Generates end-to-end tests with Playwright';
  testFramework = 'playwright';

  generate(context: GeneratorContext): GenerationResult {
    const { workflow, outputDirectory } = context;
    const files: GeneratedFile[] = [];

    if (!workflow.testing?.e2e_tests) {
      return { files: [] };
    }

    // Find the frontend component for test location
    const frontendComponent = Object.entries(workflow.stack).find(([_, c]) => c.type === 'frontend');
    if (!frontendComponent) {
      return {
        files: [],
        warnings: ['No frontend component found for E2E tests'],
      };
    }

    const [componentName] = frontendComponent;

    // Generate test file for each E2E test
    for (const test of workflow.testing.e2e_tests) {
      files.push(this.generateE2ETest(
        componentName,
        test,
        outputDirectory
      ));
    }

    // Generate Playwright config
    files.push(this.generatePlaywrightConfig(componentName, outputDirectory));

    // Generate test fixtures and helpers
    files.push(this.generateTestFixtures(componentName, outputDirectory));

    return { files };
  }

  private generateE2ETest(
    componentName: string,
    test: any,
    outputDir: string
  ): GeneratedFile {
    const testFileName = this.toKebabCase(test.name);

    let content = `import { test, expect } from '@playwright/test';

test.describe('${test.name}', () => {
  test('${test.name}', async ({ page }) => {
`;

    // Generate code for each step
    for (const step of test.steps) {
      content += this.generateStepCode(step);
    }

    content += `  });
});
`;

    return {
      path: path.join(outputDir, componentName, 'e2e', `${testFileName}.spec.ts`),
      content,
      overwrite: true,
    };
  }

  private generateStepCode(step: string): string {
    const lowerStep = step.toLowerCase();

    // Navigate to URL
    if (lowerStep.startsWith('navigate to') || lowerStep.startsWith('go to')) {
      const urlMatch = step.match(/\/[\w/-]+/);
      const url = urlMatch ? urlMatch[0] : '/';
      return `    await page.goto('${url}');\n\n`;
    }

    // Fill field
    if (lowerStep.startsWith('fill') || lowerStep.startsWith('enter')) {
      const fieldMatch = step.match(/fill\s+(\w+)\s+field\s+with\s+"([^"]+)"/i);
      const enterMatch = step.match(/enter\s+"([^"]+)"\s+(?:in|into)\s+(\w+)/i);

      if (fieldMatch) {
        const [, fieldName, value] = fieldMatch;
        return `    await page.fill('[name="${fieldName}"]', '${value}');\n`;
      } else if (enterMatch) {
        const [, value, fieldName] = enterMatch;
        return `    await page.fill('[name="${fieldName}"]', '${value}');\n`;
      }
    }

    // Click button/element
    if (lowerStep.startsWith('click')) {
      const buttonMatch = step.match(/click\s+(?:the\s+)?(\w+(?:\s+\w+)*)\s+button/i);
      const elementMatch = step.match(/click\s+"([^"]+)"/i);

      if (buttonMatch) {
        const [, buttonText] = buttonMatch;
        return `    await page.click('button:has-text("${buttonText}")');\n\n`;
      } else if (elementMatch) {
        const [, text] = elementMatch;
        return `    await page.click(':has-text("${text}")');\n\n`;
      }
    }

    // Assert URL
    if (lowerStep.startsWith('assert url') || lowerStep.includes('url is') || lowerStep.includes('should be at')) {
      const urlMatch = step.match(/\/[\w/-]+/);
      const url = urlMatch ? urlMatch[0] : '/';
      return `    await expect(page).toHaveURL('${url}');\n\n`;
    }

    // Assert text visible
    if (lowerStep.startsWith('assert') && (lowerStep.includes('visible') || lowerStep.includes('displayed'))) {
      const textMatch = step.match(/["']([^"']+)["']/);
      if (textMatch) {
        const [, text] = textMatch;
        return `    await expect(page.locator('text=${text}')).toBeVisible();\n\n`;
      }
    }

    // Wait
    if (lowerStep.startsWith('wait')) {
      return `    await page.waitForTimeout(1000);\n\n`;
    }

    // Generic step (add as comment)
    return `    // ${step}\n`;
  }

  private generatePlaywrightConfig(componentName: string, outputDir: string): GeneratedFile {
    const content = `import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',

  use: {
    baseURL: 'http://localhost:5173',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
  },

  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },

    // Mobile viewports
    {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
    },
    {
      name: 'Mobile Safari',
      use: { ...devices['iPhone 12'] },
    },
  ],

  webServer: {
    command: 'npm run dev',
    url: 'http://localhost:5173',
    reuseExistingServer: !process.env.CI,
  },
});
`;

    return {
      path: path.join(outputDir, componentName, 'playwright.config.ts'),
      content,
      overwrite: false,
    };
  }

  private generateTestFixtures(componentName: string, outputDir: string): GeneratedFile {
    const content = `import { test as base } from '@playwright/test';

/**
 * Custom test fixtures
 * Extend with authentication, database setup, etc.
 */

type TestFixtures = {
  authenticatedPage: any;
};

export const test = base.extend<TestFixtures>({
  authenticatedPage: async ({ page }, use) => {
    // Login before each test
    await page.goto('/login');
    await page.fill('[name="email"]', 'test@example.com');
    await page.fill('[name="password"]', 'password123');
    await page.click('button:has-text("Log In")');
    await page.waitForURL('/dashboard');

    await use(page);
  },
});

export { expect } from '@playwright/test';
`;

    return {
      path: path.join(outputDir, componentName, 'e2e', 'fixtures.ts'),
      content,
      overwrite: true,
    };
  }

  private toKebabCase(str: string): string {
    return str.toLowerCase().replace(/\s+/g, '-');
  }
}
