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 IActorElementCommandOptions {
  readonly cwd?: string;
  readonly outputDir?: string;
}

export interface IActorElementCommandResult {
  readonly rootDir: string;
  readonly actorClassName: string;
  readonly elementClassName: string;
}

function actorTemplate(className: string, matrixActorImport: string): string {
  return [
    `import { MatrixActor } from ${JSON.stringify(matrixActorImport)};`,
    '',
    `export class ${className} extends MatrixActor {`,
    '  static override accepts = {',
    "    getValue: {},",
    '  };',
    '',
    '  protected onGetValue(): { value: number } {',
    '    return { value: 42 };',
    '  }',
    '}',
    '',
  ].join('\n');
}

function elementTemplate(className: string, matrixActorImport: string): string {
  return [
    `import { MatrixActor } from ${JSON.stringify(matrixActorImport)};`,
    '',
    `export class ${className} extends MatrixActor {`,
    '  static override accepts = {',
    "    render: {},",
    '  };',
    '',
    '  protected onRender(): { ok: boolean } {',
    '    return { ok: true };',
    '  }',
    '}',
    '',
  ].join('\n');
}

export async function actorElementCommand(
  name: string,
  options: IActorElementCommandOptions = {}
): Promise<IActorElementCommandResult> {
  const normalizedName = toKebabCase(name);
  if (!normalizedName) {
    throw new Error('actor-element name must not be empty');
  }

  const actorClassName = `${toPascalCase(normalizedName)}Actor`;
  const elementClassName = `${toPascalCase(normalizedName)}Element`;
  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: `${actorClassName} + ${elementClassName} generated by mx actor-element`,
    runtime: { language: 'typescript', entry: 'dist/index.js' },
    components: [
      {
        type: actorClassName,
        export: actorClassName,
        mount: `${normalizedName}.actor`,
        autoStart: true,
      },
      {
        type: elementClassName,
        export: elementClassName,
        mount: `${normalizedName}.element`,
        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', `${actorClassName}.ts`), actorTemplate(actorClassName, matrixActorImport), 'utf8');
  fs.writeFileSync(path.join(rootDir, 'src', `${elementClassName}.ts`), elementTemplate(elementClassName, matrixActorImport), 'utf8');
  fs.writeFileSync(
    path.join(rootDir, 'src', 'index.ts'),
    `export { ${actorClassName} } from './${actorClassName}.js';\nexport { ${elementClassName} } from './${elementClassName}.js';\n`,
    'utf8'
  );
  writeJsonFile(path.join(rootDir, 'examples', 'query-value.mxq'), {
    label: 'Query actor value',
    operation: 'getValue',
    payload: {},
  });
  writeJsonFile(path.join(rootDir, 'examples', 'render-element.mxq'), {
    label: 'Render element',
    operation: 'render',
    payload: {},
  });
  fs.writeFileSync(
    path.join(rootDir, 'skills', `${normalizedName}.skill.md`),
    ['---', `name: ${normalizedName}`, 'intents:', '  - action: query', '    operation: getValue', '---', '', '# actor-element skill', ''].join('\n'),
    'utf8'
  );

  console.log(`Created actor-element scaffold at ${rootDir}`);
  return { rootDir, actorClassName, elementClassName };
}
