import type { Editor } from '@milkdown/kit/core';

import { remarkStringifyOptionsCtx } from '@milkdown/kit/core';
import { $nodeSchema, $remark } from '@milkdown/kit/utils';

const FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;

interface MdastNode {
  children?: unknown[];
  position?: {
    end?: { offset?: number };
    start?: { offset?: number };
  };
  type?: string;
  value?: unknown;
}

interface MdastTree {
  children?: MdastNode[];
}

interface MdastFile {
  value?: unknown;
}

function getStringValue(node: unknown): string {
  if (typeof node !== 'object' || node === null || !('value' in node)) {
    return '';
  }
  return typeof node.value === 'string' ? node.value : '';
}

/**
 * 自实现的 frontmatter remark 插件：在 mdast 阶段把文档开头的
 * `---\n...\n---` 包装成 `yaml` 节点，避免默认 remark 把它解析成
 * thematic break + 段落文本。
 *
 * 注意：`$remark` 工厂需要三层函数：`(ctx) => (processor) => (tree, file) => void`。
 */
export const remarkFrontmatterMilkdown = $remark(
  'remarkFrontmatter',
  () => () => (tree: MdastTree, file: MdastFile) => {
    const value = String(file.value ?? '');
    const match = FRONTMATTER_PATTERN.exec(value);
    if (!match) return;
    const endOffset = match[0].length;
    const yamlNode = {
      type: 'yaml',
      value: match[1] || '',
    };
    const children = Array.isArray(tree.children) ? tree.children : [];
    const filtered = children.filter((child) => {
      const start = child.position?.start?.offset ?? Number.POSITIVE_INFINITY;
      return start >= endOffset;
    });
    tree.children = [yamlNode as MdastNode, ...filtered];
  },
);

/**
 * 把 mdast `yaml` 节点映射为 ProseMirror 块节点，作为只读原子块渲染，
 * 序列化时再写回 mdast `yaml` 节点。这样 SKILL.md 的 frontmatter 在实时
 * 编辑模式下既能可视化呈现，又不会被 Milkdown 改写破坏结构。
 */
export const frontmatterSchema = $nodeSchema('frontmatter', () => ({
  group: 'block',
  atom: true,
  isolating: true,
  defining: true,
  draggable: false,
  allowGapCursor: false,
  content: '',
  attrs: {
    value: { default: '' },
  },
  parseDOM: [
    {
      tag: 'div[data-frontmatter]',
      getAttrs: (dom) => ({
        value: (dom as HTMLElement).dataset.value || '',
      }),
    },
  ],
  toDOM: (node) => [
    'div',
    {
      'data-frontmatter': '',
      'data-value': node.attrs.value,
      contenteditable: 'false',
      class: 'milkdown-frontmatter',
      role: 'button',
      tabindex: '0',
      title: '点击进入源码模式编辑',
      'aria-label': 'Skill 元数据，点击进入源码模式编辑',
    },
    ['div', { class: 'milkdown-frontmatter__title' }, 'Skill 元数据'],
    ['pre', { class: 'milkdown-frontmatter__code' }, node.attrs.value],
  ],
  parseMarkdown: {
    match: (node) => node.type === 'yaml',
    runner: (state, node, type) => {
      state.addNode(type, { value: getStringValue(node) });
    },
  },
  toMarkdown: {
    match: (node) => node.type.name === 'frontmatter',
    runner: (state, node) => {
      state.addNode('yaml', undefined, node.attrs.value as string);
    },
  },
}));

/**
 * 在 Crepe 创建前安装 frontmatter 支持。
 *
 * 在 {@link import('@milkdown/crepe').Crepe} 实例化后、`create()` 之前调用：
 * `crepe.addFeature(installFrontmatter)`，或直接 `crepe.editor.use(...)`。
 */
export function installFrontmatter(editor: Editor) {
  editor.config((ctx) => {
    ctx.update(remarkStringifyOptionsCtx, (prev) => ({
      ...prev,
      handlers: {
        ...(prev as { handlers?: Record<string, unknown> }).handlers,
        yaml: (node: { value?: unknown }) =>
          `---\n${typeof node.value === 'string' ? node.value : ''}\n---\n\n`,
      },
    }));
  });
  editor.use(remarkFrontmatterMilkdown);
  editor.use(frontmatterSchema);
}
