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

export interface IElementCommandResult {
  readonly rootDir: string;
  readonly className: string;
  readonly mount: string;
}

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; kind: string } {',
    "    return { ok: true, kind: 'element' };",
    '  }',
    '}',
    '',
  ].join('\n');
}

export async function elementCommand(name: string, options: IElementCommandOptions = {}): Promise<IElementCommandResult> {
  const normalizedName = toKebabCase(name);
  if (!normalizedName) {
    throw new Error('Element name must not be empty');
  }

  const className = `${toPascalCase(normalizedName)}Element`;
  const mount = normalizedName;
  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: `${className} generated by mx element`,
    runtime: { language: 'typescript', entry: 'dist/index.js' },
    components: [
      {
        type: className,
        export: className,
        mount,
        autoStart: true,
        props: {
          surface: 'element',
        },
      },
    ],
    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', `${className}.ts`), elementTemplate(className, matrixActorImport), 'utf8');
  fs.writeFileSync(path.join(rootDir, 'src', 'index.ts'), `export { ${className} } from './${className}.js';\n`, 'utf8');
  writeJsonFile(path.join(rootDir, 'examples', 'render.mxq'), {
    label: 'Render Element',
    operation: 'render',
    payload: {},
  });
  fs.writeFileSync(
    path.join(rootDir, 'skills', `${normalizedName}.skill.md`),
    ['---', `name: ${normalizedName}`, 'intents:', '  - action: render', '    operation: render', '---', '', `# ${className}`, ''].join('\n'),
    'utf8'
  );
  fs.writeFileSync(
    path.join(rootDir, 'tests', `${normalizedName}.spec.ts`),
    [
      "import { describe, it } from 'node:test';",
      "import { strict as assert } from 'node:assert';",
      `import { ${className} } from '../src/${className}.ts';`,
      '',
      `describe('${className}', () => {`,
      "  it('declares render command', () => {",
      `    assert.ok(Object.prototype.hasOwnProperty.call(${className}.accepts, 'render'));`,
      '  });',
      '});',
      '',
    ].join('\n'),
    'utf8'
  );

  console.log(`Created element scaffold at ${rootDir}`);
  return { rootDir, className, mount };
}
