/**
 * CodeBlock Node Extension
 * 代码块节点
 */

import { NodeExtension } from '../../core/Extension';
import { textblockTypeInputRule } from 'prosemirror-inputrules';
import { setBlockType } from 'prosemirror-commands';
import type { NodeSpec, Schema, InputRule, Command } from '../../core/types';

export interface CodeBlockOptions {
  languageClassPrefix?: string;
}

export class CodeBlock extends NodeExtension<CodeBlockOptions> {
  get name() {
    return 'codeBlock';
  }

  get defaultOptions(): CodeBlockOptions {
    return {
      languageClassPrefix: 'language-',
    };
  }

  get schema(): NodeSpec {
    return {
      content: 'text*',
      marks: '',
      group: 'block',
      code: true,
      defining: true,
      attrs: {
        language: { default: '' },
      },
      parseDOM: [
        {
          tag: 'pre',
          preserveWhitespace: 'full',
          getAttrs(dom) {
            const element = dom as HTMLElement;
            const code = element.querySelector('code');
            if (code) {
              const className = code.className || '';
              const match = className.match(/language-(\w+)/);
              return { language: match ? match[1] : '' };
            }
            return { language: '' };
          },
        },
      ],
      toDOM(node) {
        const language = node.attrs.language;
        const className = language
          ? `${this.options?.languageClassPrefix || 'language-'}${language}`
          : '';
        return [
          'pre',
          { class: 'editor-code-block' },
          ['code', { class: className }, 0],
        ];
      },
    };
  }

  inputRules(schema: Schema): InputRule[] {
    const type = schema.nodes[this.name];
    if (!type) return [];

    return [
      // ``` 或 ```language 开头转换为代码块
      textblockTypeInputRule(
        /^```([a-z]*)?[\s\n]$/,
        type,
        (match) => ({ language: match[1] || '' })
      ),
    ];
  }

  keys(schema: Schema): Record<string, Command> {
    const type = schema.nodes[this.name];
    if (!type) return {};

    return {
      'Mod-Alt-c': setBlockType(type),
    };
  }
}

export default CodeBlock;
