import chalk from 'chalk';
import { Command } from 'commander';
import { mkdir, readdir, writeFile } from 'fs/promises';
import inquirer from 'inquirer';
import path from 'node:path';

const validatePieceName = async (pieceName: string) => {
  console.log(chalk.yellow('Validating piece name....'));
  const pieceNamePattern = /^(?![._])[a-z0-9-]{1,214}$/;
  if (!pieceNamePattern.test(pieceName)) {
    console.log(
      chalk.red(
        `🚨 Invalid piece name: ${pieceName}. Piece names can only contain lowercase letters, numbers, and hyphens.`
      )
    );
    process.exit(1);
  }
};

const validatePackageName = async (packageName: string) => {
  console.log(chalk.yellow('Validating package name....'));
  const packageNamePattern = /^(?:@[a-zA-Z0-9-]+\/)?[a-zA-Z0-9-]+$/;
  if (!packageNamePattern.test(packageName)) {
    console.log(
      chalk.red(
        `🚨 Invalid package name: ${packageName}. Package names can only contain lowercase letters, numbers, and hyphens.`
      )
    );
    process.exit(1);
  }
};

const checkIfPieceExists = async (pieceName: string, pieceType: string) => {
  const piecePath = path.resolve('packages', 'pieces', pieceType, pieceName);
  try {
    await readdir(piecePath);
    console.log(chalk.red(`🚨 Piece already exists at ${piecePath}`));
    process.exit(1);
  } catch {
    // Directory does not exist, which is expected
  }
};

function capitalizeFirstLetter(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
}

const scaffoldPiece = async (
  pieceName: string,
  packageName: string,
  pieceType: string
) => {
  const baseDir = path.resolve('packages', 'pieces', pieceType, pieceName);
  const srcDir = path.join(baseDir, 'src');
  const libDir = path.join(srcDir, 'lib');
  const i18nDir = path.join(srcDir, 'i18n');

  // Create directory structure
  await mkdir(libDir, { recursive: true });
  await mkdir(i18nDir, { recursive: true });

  // Create package.json
  const packageJson = {
    name: packageName,
    version: '0.0.1',
    type: 'commonjs',
    main: './dist/src/index.js',
    types: './dist/src/index.d.ts',
    dependencies: {
      '@activepieces/pieces-common': 'workspace:*',
      '@activepieces/pieces-framework': 'workspace:*',
      '@activepieces/core-piece-types': 'workspace:*',
      '@activepieces/core-utils': 'workspace:*',
    },
    devDependencies: {
      tslib: '2.6.2',
    },
    scripts: {
      build: 'tsc -p tsconfig.lib.json && cp package.json dist/',
      bundle: 'node ../../../../dist/packages/cli/src/index.js pieces bundle',
      lint: "eslint 'src/**/*.ts'",
    },
  };
  await writeFile(
    path.join(baseDir, 'package.json'),
    JSON.stringify(packageJson, null, 2) + '\n'
  );

  // Create tsconfig.json
  const tsconfig = {
    extends: '../../../../tsconfig.base.json',
    compilerOptions: {
      module: 'commonjs',
      forceConsistentCasingInFileNames: true,
      strict: true,
      noImplicitOverride: true,
      noPropertyAccessFromIndexSignature: true,
      noImplicitReturns: true,
      noFallthroughCasesInSwitch: true,
    },
    files: [],
    include: [],
    references: [{ path: './tsconfig.lib.json' }],
  };
  await writeFile(
    path.join(baseDir, 'tsconfig.json'),
    JSON.stringify(tsconfig, null, 2) + '\n'
  );

  // Create tsconfig.lib.json
  const tsconfigLib = {
    extends: './tsconfig.json',
    compilerOptions: {
      rootDir: '.',
      baseUrl: '.',
      paths: {},
      outDir: './dist',
      declaration: true,
      declarationMap: true,
      types: ['node'],
    },
    include: ['src/**/*.ts'],
    exclude: ['jest.config.ts', 'src/**/*.spec.ts', 'src/**/*.test.ts'],
  };
  await writeFile(
    path.join(baseDir, 'tsconfig.lib.json'),
    JSON.stringify(tsconfigLib, null, 2) + '\n'
  );

  // Create .eslintrc.json
  const eslintConfig = {
    extends: ['../../../../.eslintrc.json'],
    ignorePatterns: ['!**/*'],
    overrides: [
      { files: ['*.ts', '*.tsx', '*.js', '*.jsx'], rules: {} },
      {
        files: ['*.ts', '*.tsx'],
        rules: {
          'no-restricted-imports': [
            'error',
            {
              patterns: [
                'lodash',
                'lodash/*',
                '@activepieces/core-*',
                '@activepieces/server*',
                '@activepieces/engine',
                '@activepieces/shared',
              ],
            },
          ],
        },
      },
      { files: ['*.js', '*.jsx'], rules: {} },
    ],
  };
  await writeFile(
    path.join(baseDir, '.eslintrc.json'),
    JSON.stringify(eslintConfig, null, 2) + '\n'
  );

  // Create index.ts
  const pieceNameCamelCase = pieceName
    .split('-')
    .map((s, i) => {
      if (i === 0) {
        return s;
      }
      return s[0].toUpperCase() + s.substring(1);
    })
    .join('');

  const indexTemplate = `import { createPiece, PieceAuth } from '@activepieces/pieces-framework';

export const ${pieceNameCamelCase} = createPiece({
  displayName: '${capitalizeFirstLetter(pieceName)}',
  description: '',
  auth: PieceAuth.None(),
  minimumSupportedRelease: '0.36.1',
  logoUrl: 'https://cdn.activepieces.com/pieces/${pieceName}.png',
  authors: [],
  actions: [],
  triggers: [],
});
`;

  await writeFile(path.join(srcDir, 'index.ts'), indexTemplate);
};

export const createPiece = async (
  pieceName: string,
  packageName: string,
  pieceType: string
) => {
  await validatePieceName(pieceName);
  await validatePackageName(packageName);
  await checkIfPieceExists(pieceName, pieceType);
  await scaffoldPiece(pieceName, packageName, pieceType);
  console.log(chalk.green('✨  Done!'));
  console.log(
    chalk.yellow(
      `The piece has been generated at: packages/pieces/${pieceType}/${pieceName}`
    )
  );
};

export const createPieceCommand = new Command('create')
  .description('Create a new piece')
  .action(async () => {
    const questions = [
      {
        type: 'input',
        name: 'pieceName',
        message: 'Enter the piece name:',
      },
      {
        type: 'input',
        name: 'packageName',
        message: 'Enter the package name:',
        default: (answers: Record<string, string>) =>
          `@activepieces/piece-${answers.pieceName}`,
        when: (answers: Record<string, string>) =>
          answers.pieceName !== undefined,
      },
      {
        type: 'list',
        name: 'pieceType',
        message: 'Select the piece type:',
        choices: ['community', 'custom'],
        default: 'community',
      },
    ];

    const answers = await inquirer.prompt(questions);
    createPiece(answers.pieceName, answers.packageName, answers.pieceType);
  });
