const path = require('path');
const fs = require('fs').promises;

const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);

const camelize = (s) => s.replace(/-./g, (x) => x[1].toUpperCase());

const prepareComponentName = (name) => capitalize(camelize(name));

async function registerIntegrations(EE = false) {
  // Read the package.json file
  const packageJsonPath = path.join(__dirname, '..', 'package.json');
  const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));

  // Get all dependencies that start with @noco-integrations/ from package.json
  const integrationDeps = {};
  const allDeps = {
    ...packageJson.dependencies,
    ...packageJson.devDependencies,
  };

  for (const [dep, _version] of Object.entries(allDeps)) {
    if (
      dep.startsWith('@noco-integrations/') &&
      dep !== '@noco-integrations/core'
    ) {
      // Extract the package name without the @noco-integrations/ prefix
      const packageName = dep.replace('@noco-integrations/', '');
      integrationDeps[packageName] = dep;
    }
  }

  // Get all local integrations from ../noco-integrations/packages
  try {
    const localIntegrationsPath = path.join(
      __dirname,
      '..',
      '..',
      'noco-integrations',
      'packages',
    );

    const localIntegrationDirs = await fs.readdir(localIntegrationsPath, {
      withFileTypes: true,
    });

    let missingDeps = [];

    for (const dirent of localIntegrationDirs) {
      if (!dirent.isDirectory()) {
        continue;
      }

      // check if package.json exists
      const packageJsonPath = path.join(
        localIntegrationsPath,
        dirent.name,
        'package.json',
      );

      const packageJsonExists = await fs
        .access(packageJsonPath)
        .then(() => true)
        .catch(() => false);

      if (!packageJsonExists) {
        console.log(
          `[registerIntegrations]: package.json not found for ${dirent.name}, skipping...`,
        );
        continue;
      }

      const packageName = dirent.name;
      integrationDeps[packageName] = `@noco-local-integrations/${packageName}`;

      // check if the dependencies of local integrations are present in the package.json
      const integrationPackageJsonPath = path.join(
        localIntegrationsPath,
        dirent.name,
        'package.json',
      );
      const integrationPackageJson = JSON.parse(
        await fs.readFile(integrationPackageJsonPath, 'utf-8'),
      );

      const dependencies = integrationPackageJson.dependencies;

      for (const [dep, version] of Object.entries(dependencies)) {
        if (dep.startsWith('@noco-integrations/')) {
          continue;
        }

        if (version === 'workspace:*') {
          continue;
        }

        if (!allDeps[dep]) {
          console.log(
            `${dirent.name} depends on ${dep} but it is not present in package.json`,
          );
          // add it to the package.json
          packageJson.dependencies[dep] = version;
          missingDeps.push(dep);
        }
      }
    }

    if (missingDeps.length > 0) {
      // sort the dependencies
      packageJson.dependencies = Object.fromEntries(
        Object.entries(packageJson.dependencies).sort((a, b) =>
          a[0].localeCompare(b[0]),
        ),
      );
      // write the package.json
      await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
      // inform the user to run pnpm install
      console.log(
        'Please run `pnpm install` to install the missing dependencies',
      );
    }
  } catch (e) {
    console.log('Local integrations directory not found skipping...');
  }

  // Generate index.ts content for standard integrations
  const integrationRoot = EE ? 'src/ee/integrations' : 'src/integrations';
  const indexPath = path.join(__dirname, '..', `${integrationRoot}/index.ts`);

  // Generate the content for index.ts
  let indexContent = `/*
  This file is auto-generated by \`pnpm run registerIntegrations\`
  !!! Do not edit this file manually !!!
*/

`;

  // Generate import statements for each integration
  const importStatements = [];
  const exportEntries = [];

  for (const [packageName, fullPackageName] of Object.entries(
    integrationDeps,
  )) {
    const componentName = prepareComponentName(packageName);
    importStatements.push(`import ${componentName} from '${fullPackageName}';`);
    exportEntries.push(componentName);
  }

  // Add all imports
  indexContent += importStatements.join('\n');
  indexContent += '\n\n';
  // Import IntegrationEntry type
  indexContent += `import type { IntegrationEntry } from '@noco-local-integrations/core';\n\n`;

  // Add export statement with array flattening
  // Some packages export an array of entries (e.g., workflow-node packages)
  indexContent += `export default [
${exportEntries
  .map((entry) => `  ...(Array.isArray(${entry}) ? ${entry} : [${entry}]),`)
  .join('\n')}
] as IntegrationEntry[];\n`;

  // Write the generated content to index.ts
  await fs.writeFile(indexPath, indexContent);

  console.log(`${integrationRoot}/index.ts has been generated successfully.`);
}

async function main() {
  // check if src/ee/integrations/index.ts exists
  const eeIndexPath = path.join(__dirname, '..', 'src/ee/integrations');
  const isEE = await fs
    .access(eeIndexPath)
    .then(() => true)
    .catch(() => false);

  if (isEE) {
    await registerIntegrations(true);
  } else {
    await registerIntegrations();
  }
}

main();
