import { describe, expect, it, beforeEach } from 'vitest';
import type { Program, SourceLocation } from '@promptscript/core';
import { CursorFormatter, CURSOR_VERSIONS } from '../formatters/cursor.js';

const createLoc = (): SourceLocation => ({
  file: 'test.prs',
  line: 1,
  column: 1,
});

const createMinimalProgram = (): Program => ({
  type: 'Program',
  uses: [],
  blocks: [],
  extends: [],
  loc: createLoc(),
});

describe('CursorFormatter', () => {
  let formatter: CursorFormatter;

  beforeEach(() => {
    formatter = new CursorFormatter();
  });

  it('should have correct name, outputPath and description', () => {
    expect(formatter.name).toBe('cursor');
    expect(formatter.outputPath).toBe('.cursor/rules/project.mdc');
    expect(formatter.description).toBe('Cursor rules (MDC with frontmatter)');
  });

  describe('convention validation', () => {
    it('should throw error when using xml convention', () => {
      const ast = createMinimalProgram();
      expect(() => formatter.format(ast, { convention: 'xml' })).toThrow(
        "Cursor formatter does not support 'xml' convention. Only 'markdown' convention is supported for Cursor targets."
      );
    });

    it('should throw error when using custom convention', () => {
      const ast = createMinimalProgram();
      expect(() => formatter.format(ast, { convention: 'custom' })).toThrow(
        "Cursor formatter does not support 'custom' convention. Only 'markdown' convention is supported for Cursor targets."
      );
    });

    it('should work with markdown convention', () => {
      const ast = createMinimalProgram();
      const result = formatter.format(ast, { convention: 'markdown' });
      expect(result.path).toBe('.cursor/rules/project.mdc');
    });

    it('should work without convention (defaults to markdown)', () => {
      const ast = createMinimalProgram();
      const result = formatter.format(ast);
      expect(result.path).toBe('.cursor/rules/project.mdc');
    });
  });

  describe('format', () => {
    it('should generate intro with default project', () => {
      const ast = createMinimalProgram();
      const result = formatter.format(ast);
      expect(result.path).toBe('.cursor/rules/project.mdc');
      expect(result.content).toContain('You are working on the project.');
    });

    it('should generate intro with project from context', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'context',
            content: {
              type: 'ObjectContent',
              properties: {
                project: 'Checkout Service',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('You are working on Checkout Service.');
    });

    it('should generate intro with project from identity fallback', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'identity',
            content: {
              type: 'TextContent',
              value: 'E-commerce platform backend',
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('You are working on E-commerce platform backend.');
    });

    it('should include organization in intro when available', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        meta: {
          type: 'MetaBlock',
          fields: { org: 'Acme Corp' },
          loc: createLoc(),
        },
        blocks: [
          {
            type: 'Block',
            name: 'context',
            content: {
              type: 'ObjectContent',
              properties: {
                project: 'Checkout Service',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('You are working on Checkout Service at Acme Corp.');
    });

    it('should generate tech stack as plain text', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'standards',
            content: {
              type: 'ObjectContent',
              properties: {
                code: {
                  languages: ['TypeScript'],
                  frameworks: ['React', 'Node.js'],
                  testing: ['Vitest'],
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Tech stack: TypeScript, React, Node.js, Vitest');
    });

    it('should generate code style section', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'standards',
            content: {
              type: 'ObjectContent',
              properties: {
                typescript: {
                  style: 'Functional React components',
                  hooks: 'Custom hooks for business logic',
                },
                naming: {
                  exports: 'Named exports only',
                  components: 'No class components',
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Code style:');
      expect(result.content).toContain('- style: Functional React components');
      expect(result.content).toContain('- hooks: Custom hooks for business logic');
      expect(result.content).toContain('- exports: Named exports only');
      expect(result.content).toContain('- components: No class components');
    });

    it('should generate code style section from array-based standards', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'standards',
            content: {
              type: 'ObjectContent',
              properties: {
                typescript: ['Use strict mode', 'Prefer const over let'],
                naming: ['Use camelCase for variables', 'Use PascalCase for types'],
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Code style:');
      expect(result.content).toContain('- Use strict mode');
      expect(result.content).toContain('- Prefer const over let');
      expect(result.content).toContain('- Use camelCase for variables');
      expect(result.content).toContain('- Use PascalCase for types');
    });

    it('should generate commands section', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/review': 'Review code for quality',
                '/test': 'Write comprehensive tests',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Commands:');
      expect(result.content).toContain('/review - Review code for quality');
      expect(result.content).toContain('/test - Write comprehensive tests');
    });

    it('should generate never section from restrictions array', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'restrictions',
            content: {
              type: 'ArrayContent',
              elements: ['Expose API keys or secrets', 'Use any types', 'Skip input validation'],
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Never:');
      expect(result.content).toContain('- Expose API keys or secrets');
      expect(result.content).toContain('- Use any types');
      expect(result.content).toContain('- Skip input validation');
    });

    it('should handle text content for never section', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'restrictions',
            content: {
              type: 'TextContent',
              value: 'Expose secrets\nSkip validation\n- Already prefixed',
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Never:');
      expect(result.content).toContain('- Expose secrets');
      expect(result.content).toContain('- Skip validation');
      expect(result.content).toContain('- Already prefixed');
    });

    it('should separate sections with double newlines (no markdown)', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        meta: {
          type: 'MetaBlock',
          fields: { org: 'Test' },
          loc: createLoc(),
        },
        blocks: [
          {
            type: 'Block',
            name: 'standards',
            content: {
              type: 'ObjectContent',
              properties: {
                code: {
                  languages: ['TypeScript'],
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
          {
            type: 'Block',
            name: 'restrictions',
            content: {
              type: 'ArrayContent',
              elements: ['No secrets'],
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('\n\n');
      expect(result.content).not.toContain('##');
      // Frontmatter uses --- but content shouldn't have additional horizontal rules
      expect(result.content).toMatch(/^---\ndescription:/);
    });

    it('should skip empty sections', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'standards',
            content: {
              type: 'ObjectContent',
              properties: {
                code: {},
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).not.toContain('Tech stack:');
      expect(result.content).not.toContain('Code style:');
    });

    it('should handle single value tech items', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'standards',
            content: {
              type: 'ObjectContent',
              properties: {
                code: {
                  languages: 'TypeScript',
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Tech stack: TypeScript');
    });

    it('should skip never section when content is ObjectContent', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'restrictions',
            content: {
              type: 'ObjectContent',
              properties: {
                key: 'value',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).not.toContain('Never:');
    });

    it('should extract never items from ObjectContent with items array', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'restrictions',
            content: {
              type: 'ObjectContent',
              properties: {
                items: ['Never use any type', 'Never skip tests', 'Never commit secrets'],
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Never:');
      expect(result.content).toContain('- Never use any type');
      expect(result.content).toContain('- Never skip tests');
      expect(result.content).toContain('- Never commit secrets');
    });

    it('should handle configuration-files with object values (extractListItems object path)', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'context',
            content: {
              type: 'ObjectContent',
              properties: {
                'configuration-files': {
                  eslint: { extends: 'strict', rules: 'custom' },
                  prettier: { tabWidth: 2, semi: true },
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Config:');
      expect(result.content).toContain('eslint: extends: strict; rules: custom');
      expect(result.content).toContain('prettier: tabWidth: 2; semi: true');
    });

    it('should handle configuration-files with string values (extractListItems string path)', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'context',
            content: {
              type: 'ObjectContent',
              properties: {
                'configuration-files': {
                  readme: 'Project documentation\nSetup instructions\n- Important notes',
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Config:');
      expect(result.content).toContain(
        'readme: Project documentation; Setup instructions; Important notes'
      );
    });

    it('should handle configuration-files with numeric values (extractListItems fallback path)', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'context',
            content: {
              type: 'ObjectContent',
              properties: {
                'configuration-files': {
                  port: 3000,
                  timeout: 5000,
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Config:');
      expect(result.content).toContain('port: 3000');
      expect(result.content).toContain('timeout: 5000');
    });

    it('should handle configuration-files with array values (extractListItems array path)', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'context',
            content: {
              type: 'ObjectContent',
              properties: {
                'configuration-files': {
                  dependencies: ['typescript', 'vitest', 'eslint'],
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Config:');
      expect(result.content).toContain('dependencies: typescript; vitest; eslint');
    });
  });

  describe('version support', () => {
    it('should expose supported versions', () => {
      const versions = CursorFormatter.getSupportedVersions();
      expect(versions.modern).toBeDefined();
      expect(versions.legacy).toBeDefined();
      expect(versions.modern.outputPath).toBe('.cursor/rules/project.mdc');
      expect(versions.legacy.outputPath).toBe('.cursorrules');
    });

    it('should have version metadata', () => {
      expect(CURSOR_VERSIONS.modern.cursorVersion).toBe('0.45+');
      expect(CURSOR_VERSIONS.legacy.deprecated).toBe(true);
    });

    describe('modern format (default)', () => {
      it('should generate MDC with frontmatter', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast);

        expect(result.path).toBe('.cursor/rules/project.mdc');
        expect(result.content).toMatch(/^---\n/);
        expect(result.content).toContain('alwaysApply: true');
        expect(result.content).toContain('---\n\n');
      });

      it('should use modern format when version is undefined', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, {});

        expect(result.path).toBe('.cursor/rules/project.mdc');
        expect(result.content).toMatch(/^---\n/);
      });

      it('should use modern format when version is "modern"', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'modern' });

        expect(result.path).toBe('.cursor/rules/project.mdc');
        expect(result.content).toMatch(/^---\n/);
      });

      it('should default to modern format for unknown version', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'unknown-version' });

        expect(result.path).toBe('.cursor/rules/project.mdc');
        expect(result.content).toMatch(/^---\n/);
        expect(result.content).toContain('alwaysApply: true');
      });
    });

    describe('legacy format', () => {
      it('should generate plain text without frontmatter', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'legacy' });

        expect(result.path).toBe('.cursorrules');
        expect(result.content).not.toMatch(/^---\n/);
        expect(result.content).not.toContain('alwaysApply');
      });

      it('should start with intro text', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'legacy' });

        expect(result.content).toMatch(/^You are working on/);
      });

      it('should allow custom output path in legacy mode', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, {
          version: 'legacy',
          outputPath: 'custom/.cursorrules',
        });

        expect(result.path).toBe('custom/.cursorrules');
      });
    });

    describe('multifile format', () => {
      it('should generate main file with alwaysApply', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'multifile' });

        expect(result.path).toBe('.cursor/rules/project.mdc');
        expect(result.content).toContain('alwaysApply: true');
      });

      it('should extract TypeScript globs and generate separate file', () => {
        const ast: Program = {
          ...createMinimalProgram(),
          blocks: [
            {
              type: 'Block',
              name: 'guards',
              content: {
                type: 'ObjectContent',
                properties: {
                  globs: ['*.ts', '*.tsx'],
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
            {
              type: 'Block',
              name: 'standards',
              content: {
                type: 'ObjectContent',
                properties: {
                  typescript: {
                    strict: 'Enable strict mode',
                  },
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
          ],
        };

        const result = formatter.format(ast, { version: 'multifile' });

        expect(result.additionalFiles).toBeDefined();
        expect(result.additionalFiles?.length).toBeGreaterThan(0);

        const tsFile = result.additionalFiles?.find((f) => f.path.includes('typescript.mdc'));
        expect(tsFile).toBeDefined();
        expect(tsFile?.content).toContain('globs:');
        expect(tsFile?.content).toContain('*.ts');
        expect(tsFile?.content).toContain('*.tsx');
        expect(tsFile?.content).toContain('TypeScript-specific rules');
      });

      it('should extract testing globs and generate separate file', () => {
        const ast: Program = {
          ...createMinimalProgram(),
          blocks: [
            {
              type: 'Block',
              name: 'guards',
              content: {
                type: 'ObjectContent',
                properties: {
                  globs: ['**/*.spec.ts', '**/__tests__/**'],
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
            {
              type: 'Block',
              name: 'standards',
              content: {
                type: 'ObjectContent',
                properties: {
                  testing: {
                    pattern: 'AAA',
                    coverage: '90%',
                  },
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
          ],
        };

        const result = formatter.format(ast, { version: 'multifile' });

        expect(result.additionalFiles).toBeDefined();

        const testFile = result.additionalFiles?.find((f) => f.path.includes('testing.mdc'));
        expect(testFile).toBeDefined();
        expect(testFile?.content).toContain('globs:');
        expect(testFile?.content).toContain('Testing-specific rules');
        expect(testFile?.content).toContain('pattern: AAA');
      });

      it('should generate python.mdc from @standards.python + .py globs', () => {
        const ast: Program = {
          ...createMinimalProgram(),
          blocks: [
            {
              type: 'Block',
              name: 'guards',
              content: {
                type: 'ObjectContent',
                properties: {
                  globs: ['**/*.py'],
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
            {
              type: 'Block',
              name: 'standards',
              content: {
                type: 'ObjectContent',
                properties: {
                  python: {
                    style: 'PEP 8',
                    typing: 'Use type hints',
                  },
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
          ],
        };

        const result = formatter.format(ast, { version: 'multifile' });

        expect(result.additionalFiles).toBeDefined();

        const pyFile = result.additionalFiles?.find((f) => f.path.includes('python.mdc'));
        expect(pyFile).toBeDefined();
        expect(pyFile?.content).toContain('**/*.py');
        expect(pyFile?.content).toContain('Python-specific rules');
        expect(pyFile?.content).toContain('PEP 8');
      });

      it('should not generate glob files when no @standards block exists', () => {
        const ast: Program = {
          ...createMinimalProgram(),
          blocks: [
            {
              type: 'Block',
              name: 'guards',
              content: {
                type: 'ObjectContent',
                properties: {
                  globs: ['**/*.ts', '**/*.py'],
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
          ],
        };

        const result = formatter.format(ast, { version: 'multifile' });

        // Filter out command files — only check for glob-based additional files
        const globFiles = result.additionalFiles?.filter((f) => !f.path.includes('commands/'));
        expect(globFiles ?? []).toHaveLength(0);
      });

      it('should not generate shortcuts.mdc in multifile mode (removed in favor of Commands section)', () => {
        const ast: Program = {
          ...createMinimalProgram(),
          blocks: [
            {
              type: 'Block',
              name: 'shortcuts',
              content: {
                type: 'ObjectContent',
                properties: {
                  '/test': 'Run tests',
                  '/deploy': {
                    description: 'Deploy workflow',
                    steps: ['Build', 'Test', 'Deploy'],
                  },
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
          ],
        };

        const result = formatter.format(ast, { version: 'multifile' });

        // shortcuts.mdc is no longer generated — shortcuts appear in the main
        // file's Commands: section and multi-line ones become .cursor/commands/*.md
        const shortcutsFile = result.additionalFiles?.find((f) => f.path.includes('shortcuts.mdc'));
        expect(shortcutsFile).toBeUndefined();
      });

      it('should not generate additional files when no guards or shortcuts', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'multifile' });

        expect(result.additionalFiles).toBeUndefined();
      });

      it('should generate rule files from @guards named entries with applyTo', () => {
        const ast: Program = {
          ...createMinimalProgram(),
          blocks: [
            {
              type: 'Block',
              name: 'guards',
              content: {
                type: 'ObjectContent',
                properties: {
                  'angular-components': {
                    applyTo: ['apps/admin/**/*.ts', 'apps/webview/**/*.ts'],
                    description: 'Angular component standards',
                    content: 'Use OnPush change detection.',
                  },
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
          ],
        };

        const result = formatter.format(ast, { version: 'multifile' });

        expect(result.additionalFiles).toBeDefined();
        const ruleFile = result.additionalFiles?.find((f) =>
          f.path.includes('angular-components.mdc')
        );
        expect(ruleFile).toBeDefined();
        expect(ruleFile?.content).toContain('globs:');
        expect(ruleFile?.content).toContain('apps/admin/**/*.ts');
        expect(ruleFile?.content).toContain('apps/webview/**/*.ts');
        expect(ruleFile?.content).toContain('Angular component standards');
        expect(ruleFile?.content).toContain('Use OnPush change detection.');
      });

      it('should handle both globs auto-split and named entries together', () => {
        const ast: Program = {
          ...createMinimalProgram(),
          blocks: [
            {
              type: 'Block',
              name: 'guards',
              content: {
                type: 'ObjectContent',
                properties: {
                  globs: ['**/*.ts'],
                  'api-rules': {
                    applyTo: ['apps/api/**/*.ts'],
                    description: 'API rules',
                    content: 'Use DI.',
                  },
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
            {
              type: 'Block',
              name: 'standards',
              content: {
                type: 'ObjectContent',
                properties: {
                  typescript: { strict: 'Enable strict' },
                },
                loc: createLoc(),
              },
              loc: createLoc(),
            },
          ],
        };

        const result = formatter.format(ast, { version: 'multifile' });

        expect(result.additionalFiles).toBeDefined();
        const tsFile = result.additionalFiles?.find((f) => f.path.includes('typescript.mdc'));
        const apiFile = result.additionalFiles?.find((f) => f.path.includes('api-rules.mdc'));
        expect(tsFile).toBeDefined();
        expect(apiFile).toBeDefined();
        expect(apiFile?.content).toContain('Use DI.');
      });

      it('should treat frontmatter version as modern', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'frontmatter' });

        expect(result.path).toBe('.cursor/rules/project.mdc');
        expect(result.content).toContain('alwaysApply: true');
        expect(result.additionalFiles).toBeUndefined();
      });

      it('should have multifile and agents-md versions in CURSOR_VERSIONS', () => {
        expect(CURSOR_VERSIONS.multifile).toBeDefined();
        expect(CURSOR_VERSIONS.multifile.name).toBe('multifile');
        expect(CURSOR_VERSIONS['agents-md']).toBeDefined();
        expect(CURSOR_VERSIONS['agents-md'].outputPath).toBe('AGENTS.md');
        // frontmatter alias has been removed from CURSOR_VERSIONS
        expect('frontmatter' in CURSOR_VERSIONS).toBe(false);
      });
    });

    describe('agents-md format (Cursor 2.4+)', () => {
      it('should output AGENTS.md path', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'agents-md' });

        expect(result.path).toBe('AGENTS.md');
      });

      it('should produce plain markdown with no YAML frontmatter', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'agents-md' });

        expect(result.content).not.toContain('---');
        expect(result.content).not.toContain('alwaysApply');
        expect(result.content).not.toContain('description:');
      });

      it('should not generate additional files', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'agents-md' });

        expect(result.additionalFiles).toBeUndefined();
      });

      it('should include common sections in plain markdown format', () => {
        const ast = createMinimalProgram();
        const result = formatter.format(ast, { version: 'agents-md' });

        expect(result.content).toContain('You are working on');
        expect(result.content.endsWith('\n')).toBe(true);
      });
    });
  });

  describe('slash commands (.cursor/commands/)', () => {
    it('should generate command files for multi-line shortcuts in modern format', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/test': 'Write unit tests using:\n- Vitest\n- AAA pattern',
                '/review': 'Review code quality',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);

      expect(result.additionalFiles).toBeDefined();
      expect(result.additionalFiles?.length).toBe(1);

      const testCommand = result.additionalFiles?.find(
        (f) => f.path === '.cursor/commands/test.md'
      );
      expect(testCommand).toBeDefined();
      expect(testCommand?.content).toBe('Write unit tests using:\n- Vitest\n- AAA pattern');
    });

    it('should not generate command files for single-line shortcuts', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/review': 'Review code quality',
                '/build': 'Build the project',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);

      expect(result.additionalFiles).toBeUndefined();
    });

    it('should generate multiple command files for multiple multi-line shortcuts', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/test': 'Write tests:\n- Unit tests\n- Integration tests',
                '/deploy': 'Deploy steps:\n1. Build\n2. Test\n3. Deploy',
                '/review': 'Single line review',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);

      expect(result.additionalFiles).toBeDefined();
      expect(result.additionalFiles?.length).toBe(2);

      const testCommand = result.additionalFiles?.find(
        (f) => f.path === '.cursor/commands/test.md'
      );
      const deployCommand = result.additionalFiles?.find(
        (f) => f.path === '.cursor/commands/deploy.md'
      );

      expect(testCommand).toBeDefined();
      expect(deployCommand).toBeDefined();
      expect(testCommand?.content).toContain('Unit tests');
      expect(deployCommand?.content).toContain('Build');
    });

    it('should remove leading slash from command file names', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/my-command': 'Line 1\nLine 2',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);

      expect(result.additionalFiles).toBeDefined();
      const command = result.additionalFiles?.[0];
      expect(command?.path).toBe('.cursor/commands/my-command.md');
      expect(command?.path).not.toContain('//');
    });

    it('should trim whitespace from command content', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/test': '  \n  Write tests:\n  - Test 1\n  ',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);

      expect(result.additionalFiles).toBeDefined();
      const command = result.additionalFiles?.[0];
      expect(command?.content).not.toMatch(/^\s/);
      expect(command?.content).not.toMatch(/\s$/);
    });

    it('should generate command files in multifile format', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/test': 'Multi-line\ncommand',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast, { version: 'multifile' });

      expect(result.additionalFiles).toBeDefined();
      const commandFile = result.additionalFiles?.find((f) =>
        f.path.startsWith('.cursor/commands/')
      );
      expect(commandFile).toBeDefined();
      expect(commandFile?.path).toBe('.cursor/commands/test.md');
    });

    it('should not generate command files in legacy format', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/test': 'Multi-line\ncommand',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast, { version: 'legacy' });

      expect(result.additionalFiles).toBeUndefined();
    });

    it('should include single-line shortcuts in Commands section of rules', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'shortcuts',
            content: {
              type: 'ObjectContent',
              properties: {
                '/review': 'Review code quality',
                '/test': 'Multi-line\ntest command',
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);

      // Single-line should be in main content
      expect(result.content).toContain('Commands:');
      expect(result.content).toContain('/review - Review code quality');
      // Multi-line first line should also appear in Commands section
      expect(result.content).toContain('/test - Multi-line');
    });
  });

  describe('examples section', () => {
    it('should render examples from @examples block', () => {
      const ast: Program = {
        ...createMinimalProgram(),
        blocks: [
          {
            type: 'Block',
            name: 'examples',
            content: {
              type: 'ObjectContent',
              properties: {
                'commit-message': {
                  input: 'Fix login bug',
                  output: 'fix(auth): resolve login redirect loop',
                  description: 'Shows conventional commit format',
                },
              },
              loc: createLoc(),
            },
            loc: createLoc(),
          },
        ],
      };

      const result = formatter.format(ast);
      expect(result.content).toContain('Examples:');
      expect(result.content).toContain('### Example: commit-message');
      expect(result.content).toContain('Shows conventional commit format');
      expect(result.content).toContain('**Input:**');
      expect(result.content).toContain('Fix login bug');
      expect(result.content).toContain('**Output:**');
      expect(result.content).toContain('fix(auth): resolve login redirect loop');
    });

    it('should not render examples section when no @examples block exists', () => {
      const ast = createMinimalProgram();
      const result = formatter.format(ast);
      expect(result.content).not.toContain('Examples:');
    });
  });
});
