const { spawn, exec } = require('child_process');
const path = require('path');
const fs = require('fs');
const puppeteer = require('puppeteer');

// Define timestamp for unique test directory
const timestamp = Date.now();
const testDir = `/tmp/drupal_test_jsonapi_query_builder_${timestamp}`;
const serverPort = 9888; // Use a different port to avoid conflicts
let serverProcess;

// Function to run a command and return its output
const runCommand = (command, cwd) => {
  return new Promise((resolve, reject) => {
    exec(command, { cwd }, (error, stdout, stderr) => {
      if (error) {
        console.error(`Error executing command: ${error.message}`);
        console.error(`stderr: ${stderr}`);
        reject(error);
        return;
      }
      resolve(stdout);
    });
  });
};

// Create test directory and set up Drupal
const setupDrupal = async () => {
  console.log(`Setting up test environment in ${testDir}...`);

  // Create test directory
  fs.mkdirSync(testDir, { recursive: true });

  // Get module root directory (2 levels up from this script)
  const moduleDir = path.resolve(__dirname, '../../..');

  try {
    // Create Drupal project
    console.log('Creating Drupal project...');
    await runCommand(`composer create-project drupal/recommended-project:^10.1 . --quiet`, testDir);

    // Install dependencies
    console.log('Installing dependencies...');
    await runCommand(`composer require --quiet drupal/openapi_jsonapi:^3.0@dev drush/drush`, testDir);

    // Create module directory
    const moduleTargetDir = `${testDir}/web/modules/contrib/jsonapi_query_builder`;
    fs.mkdirSync(moduleTargetDir, { recursive: true });

    // Copy module files
    console.log('Copying module files...');
    await runCommand(`cp -r ${moduleDir}/* ${moduleTargetDir}/`, testDir);
    try {
      await runCommand(`cp -r ${moduleDir}/.git* ${moduleTargetDir}/`, testDir);
    } catch (e) {
      // Ignore error if .git* files don't exist
    }

    // Clean up unnecessary files
    await runCommand(`rm -rf ${moduleTargetDir}/vendor ${moduleTargetDir}/release ${moduleTargetDir}/test-install.sh`, testDir);

    // Build React app
    console.log('Building React app...');
    await runCommand(`yarn install && yarn build`, `${moduleTargetDir}/js/react-app`);

    // Install Drupal with standard profile which includes the article content type
    console.log('Installing Drupal...');
    await runCommand(`vendor/bin/drush site:install standard --yes --db-url=sqlite://sites/default/files/.ht.sqlite --account-name=admin --account-pass=admin --site-name="JSON:API Query Builder Test"`, testDir);

    // Enable modules
    console.log('Enabling modules...');
    await runCommand(`vendor/bin/drush en -y jsonapi openapi_jsonapi jsonapi_query_builder`, testDir);

    // For simplicity, let's skip creating custom content
    console.log('Skipping content creation to simplify testing...');

    // Clear caches
    await runCommand(`vendor/bin/drush cr`, testDir);

    return true;
  } catch (error) {
    console.error('Error setting up Drupal:', error);
    return false;
  }
};

// Start PHP server
const startServer = () => {
  return new Promise((resolve) => {
    console.log(`Starting PHP server on port ${serverPort}...`);
    serverProcess = spawn('php', ['-S', `localhost:${serverPort}`], {
      cwd: `${testDir}/web`,
      stdio: 'pipe'
    });

    // Give the server some time to start
    setTimeout(() => {
      console.log('Server is ready.');
      resolve();
    }, 3000);

    serverProcess.stdout.on('data', (data) => {
      console.log(`Server output: ${data}`);
    });

    serverProcess.stderr.on('data', (data) => {
      console.error(`Server error: ${data}`);
    });
  });
};

// Run e2e tests
const runTests = async () => {
  try {
    const moduleDir = path.resolve(__dirname, '../../..');

    // Give the server some time to start
    await new Promise(resolve => setTimeout(resolve, 2000));

    console.log('Running e2e tests...');

    try {
      // Create screenshots directory if it doesn't exist
      const screenshotsDir = path.join(moduleDir, 'js/react-app/tests/screenshots');
      if (!fs.existsSync(screenshotsDir)) {
        fs.mkdirSync(screenshotsDir, { recursive: true });
      }

      // First, generate screenshots for documentation
      console.log('Taking screenshots for documentation...');
      await generateDocumentationScreenshots(moduleDir, screenshotsDir, serverPort);

      // Then run the actual e2e tests using Jest
      console.log('Running e2e tests with Jest...');

      // Set environment variables for the tests
      process.env.TEST_URL = `http://localhost:${serverPort}`;
      process.env.TEST_USERNAME = 'admin';
      process.env.TEST_PASSWORD = 'admin';

      // Run the Jest tests using child_process.spawn
      return new Promise((resolve) => {
        const jestProcess = spawn('npx', ['jest', 'tests/e2e-test.js', '--config=jest.config.js'], {
          cwd: path.join(moduleDir, 'js/react-app'),
          stdio: 'inherit',
          env: {
            ...process.env,
            HEADLESS: 'new',
            TEST_URL: `http://localhost:${serverPort}`,
            TEST_USERNAME: 'admin',
            TEST_PASSWORD: 'admin'
          }
        });

        jestProcess.on('close', (code) => {
          if (code === 0) {
            console.log('✅ E2E tests completed successfully!');
            resolve(true);
          } else {
            console.error(`❌ E2E tests failed with exit code ${code}`);
            resolve(false);
          }
        });
      });
    } catch (error) {
      console.error('Error running tests:', error);
      return false;
    }
  } catch (error) {
    console.error('Error running tests:', error);
    return false;
  }
};

// Function to generate documentation screenshots
const generateDocumentationScreenshots = async (moduleDir, screenshotsDir, serverPort) => {
  // Launch a browser for screenshots
  console.log('Launching browser for screenshots...');
  const browser = await puppeteer.launch({
    headless: 'new',
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });
  const page = await browser.newPage();

  try {
    // Set viewport to a desktop size (standard laptop)
    await page.setViewport({ width: 1280, height: 800 });

    // Navigate to login page
    console.log('Logging in...');
    await page.goto(`http://localhost:${serverPort}/user/login`);

    // Log in with admin credentials
    await page.type('#edit-name', 'admin');
    await page.type('#edit-pass', 'admin');
    await page.click('#edit-submit');

    // Wait for login to complete
    await page.waitForSelector('body.user-logged-in', { timeout: 10000 });

    // Navigate to the JSON:API Query Builder
    console.log('Navigating to JSON:API Query Builder...');
    await page.goto(`http://localhost:${serverPort}/admin/config/services/jsonapi-query-builder`);

    // Wait for the app to load
    await page.waitForSelector('#jsonapi-query-builder-root, .jsonapi-explorer-container, .jsonapi-query-builder-container', { visible: true, timeout: 10000 });

    // Take a desktop screenshot
    console.log('Taking desktop screenshot...');
    await page.screenshot({
      path: path.join(screenshotsDir, 'jsonapi-query-builder-desktop.png'),
      fullPage: true
    });

    // Resize to tablet size
    await page.setViewport({ width: 768, height: 1024 });

    // Take a tablet screenshot
    console.log('Taking tablet screenshot...');
    await page.screenshot({
      path: path.join(screenshotsDir, 'jsonapi-query-builder-tablet.png'),
      fullPage: true
    });

    // Resize to mobile view for responsive screenshot
    console.log('Testing responsive layout...');
    await page.setViewport({ width: 375, height: 667 });

    // Take mobile screenshot
    console.log('Taking mobile screenshot...');
    await page.screenshot({
      path: path.join(screenshotsDir, 'jsonapi-query-builder-mobile.png'),
      fullPage: true
    });

    // Create the documentation directory if it doesn't exist
    console.log('Copying screenshots to documentation directory...');
    const docsDir = path.join(moduleDir, 'images');
    if (!fs.existsSync(docsDir)) {
      fs.mkdirSync(docsDir, { recursive: true });
    }

    // Copy screenshots with proper names for documentation
    fs.copyFileSync(
      path.join(screenshotsDir, 'jsonapi-query-builder-desktop.png'),
      path.join(docsDir, 'jsonapi-query-builder-interface.png')
    );

    fs.copyFileSync(
      path.join(screenshotsDir, 'jsonapi-query-builder-tablet.png'),
      path.join(docsDir, 'jsonapi-query-builder-fields.png')
    );

    fs.copyFileSync(
      path.join(screenshotsDir, 'jsonapi-query-builder-mobile.png'),
      path.join(docsDir, 'jsonapi-query-builder-responsive.png')
    );

    console.log('✅ Screenshots captured and copied to documentation directory!');
    return true;
  } catch (error) {
    console.error('Error during screenshot capture:', error);
    return false;
  } finally {
    // Close the browser
    await browser.close();
  }
};

// Cleanup function
const cleanup = () => {
  if (serverProcess) {
    console.log('Stopping server...');
    serverProcess.kill();
  }

  console.log(`Cleaning up test directory (${testDir})...`);
  // Don't block on this, just try to clean up
  exec(`rm -rf ${testDir}`, (error) => {
    if (error) {
      console.warn(`Could not remove test directory: ${error.message}`);
      console.warn(`You may need to manually remove: ${testDir}`);
    } else {
      console.log('Cleanup complete.');
    }
  });
};

// Main execution
(async () => {
  try {
    // Setup Drupal
    const setupSuccess = await setupDrupal();
    if (!setupSuccess) {
      console.error('Failed to set up Drupal environment.');
      cleanup();
      process.exit(1);
    }

    // Start server
    await startServer();

    // Run tests
    const testSuccess = await runTests();

    // Cleanup
    cleanup();

    // Exit with appropriate code
    process.exit(testSuccess ? 0 : 1);
  } catch (error) {
    console.error('Error:', error);
    cleanup();
    process.exit(1);
  }
})();

// Handle termination signals
process.on('SIGINT', () => {
  console.log('Received SIGINT, cleaning up...');
  cleanup();
  process.exit(1);
});

process.on('SIGTERM', () => {
  console.log('Received SIGTERM, cleaning up...');
  cleanup();
  process.exit(1);
});
