import * as fs from 'node:fs';
import * as path from 'node:path';
import { toKebabCase, toPascalCase, validateMatrixManifest, writeJsonFile, generatePackageJson, generateTsconfig } from '../utils/scaffold.js';

export interface ICqrsCommandOptions {
  readonly cwd?: string;
  readonly outputDir?: string;
}

export interface ICqrsCommandResult {
  readonly rootDir: string;
  readonly commandClassName: string;
  readonly queryClassName: string;
}

function actorTemplate(className: string, operation: string, matrixActorImport: string): string {
  return [
    `import { MatrixActor } from ${JSON.stringify(matrixActorImport)};`,
    '',
    `export class ${className} extends MatrixActor {`,
    '  static override accepts = {',
    `    ${operation}: {},`,
    '  };',
    '',
    `  protected on${operation.charAt(0).toUpperCase()}${operation.slice(1)}(): { ok: boolean } {`,
    '    return { ok: true };',
    '  }',
    '}',
    '',
  ].join('\n');
}

export async function cqrsCommand(name: string, options: ICqrsCommandOptions = {}): Promise<ICqrsCommandResult> {
  const normalizedName = toKebabCase(name);
  if (!normalizedName) {
    throw new Error('cqrs name must not be empty');
  }

  const baseName = toPascalCase(normalizedName);
  const commandClassName = `${baseName}CommandActor`;
  const queryClassName = `${baseName}QueryActor`;
  const cwd = path.resolve(options.cwd ?? process.cwd());
  const rootDir = path.resolve(options.outputDir ?? path.join(cwd, normalizedName));
  const matrixActorImport = '@open-matrix/core';

  fs.mkdirSync(path.join(rootDir, 'src'), { recursive: true });
  fs.mkdirSync(path.join(rootDir, 'examples'), { recursive: true });
  fs.mkdirSync(path.join(rootDir, 'skills'), { recursive: true });
  fs.mkdirSync(path.join(rootDir, 'tests'), { recursive: true });

  const manifest = {
    name: normalizedName,
    version: '0.1.0',
    description: `${baseName} CQRS package generated by mx cqrs`,
    runtime: { language: 'typescript', entry: 'dist/index.js' },
    components: [
      {
        type: commandClassName,
        export: commandClassName,
        mount: `${normalizedName}.command`,
        autoStart: true,
      },
      {
        type: queryClassName,
        export: queryClassName,
        mount: `${normalizedName}.query`,
        autoStart: true,
      },
    ],
    permissions: { fsPolicy: 'none' },
  };

  const validation = validateMatrixManifest(manifest);
  if (!validation.valid) {
    throw new Error(`Generated matrix.json failed validation: ${validation.errors.join('; ')}`);
  }

  writeJsonFile(path.join(rootDir, 'matrix.json'), manifest);
  writeJsonFile(path.join(rootDir, 'package.json'), generatePackageJson(normalizedName));
  writeJsonFile(path.join(rootDir, 'tsconfig.json'), generateTsconfig());
  fs.writeFileSync(
    path.join(rootDir, 'src', `${commandClassName}.ts`),
    actorTemplate(commandClassName, 'execute', matrixActorImport),
    'utf8'
  );
  fs.writeFileSync(
    path.join(rootDir, 'src', `${queryClassName}.ts`),
    actorTemplate(queryClassName, 'query', matrixActorImport),
    'utf8'
  );
  fs.writeFileSync(
    path.join(rootDir, 'src', 'index.ts'),
    `export { ${commandClassName} } from './${commandClassName}.js';\nexport { ${queryClassName} } from './${queryClassName}.js';\n`,
    'utf8'
  );
  writeJsonFile(path.join(rootDir, 'examples', 'execute.mxq'), {
    label: 'Execute command',
    operation: 'execute',
    payload: {},
  });
  writeJsonFile(path.join(rootDir, 'examples', 'query.mxq'), {
    label: 'Query projection',
    operation: 'query',
    payload: {},
  });
  fs.writeFileSync(
    path.join(rootDir, 'skills', `${normalizedName}.skill.md`),
    ['---', `name: ${normalizedName}`, 'intents:', '  - action: command', '    operation: execute', '---', '', '# cqrs skill', ''].join('\n'),
    'utf8'
  );

  console.log(`Created cqrs scaffold at ${rootDir}`);
  return { rootDir, commandClassName, queryClassName };
}
