import { describe, it, expect, beforeAll } from 'vitest';
import { markdownService } from '../../services/markdownService';
import {
  MarkdownNodeType,
  ValidationSeverity,
  LinkType,
} from '../../types/markdown';

// 集成测试 - 需要Tauri后端运行
describe('Markdown Parser Integration Tests', () => {
  const complexMarkdown = `
# 主标题

这是一个复杂的Markdown文档示例，用于测试Tree-sitter解析器的各种功能。

## 文本格式

这里有**粗体文本**、*斜体文本*和\`内联代码\`。

### 链接和图片

- [Google](https://google.com "Google搜索")
- [GitHub](https://github.com)
- ![示例图片](https://example.com/image.png "示例图片")

## 列表

### 无序列表
- 项目1
- 项目2
  - 子项目2.1
  - 子项目2.2
- 项目3

### 有序列表
1. 第一项
2. 第二项
3. 第三项

## 代码块

\`\`\`javascript
function hello() {
    console.log("Hello, World!");
    return "success";
}

hello();
\`\`\`

\`\`\`python
def greet(name):
    return f"Hello, {name}!"

print(greet("World"))
\`\`\`

## 引用

> 这是一个引用块。
> 
> 它可以包含多行内容。

## 表格

| 列1 | 列2 | 列3 |
|-----|-----|-----|
| 数据1 | 数据2 | 数据3 |
| 数据4 | 数据5 | 数据6 |

## 水平分割线

---

## 结论

这个文档展示了Markdown的各种语法特性。

### 嵌套标题测试

#### 四级标题

##### 五级标题

###### 六级标题
`;

  const malformedMarkdown = `
# 标题1

## 标题2

#### 跳过了三级标题

[空链接]()

**未闭合的粗体

\`\`\`
未指定语言的代码块
console.log("test");
\`\`\`
`;

  beforeAll(async () => {
    // 确保服务可用
    try {
      await markdownService.parseMarkdown('# Test');
    } catch (error) {
      console.warn('Tauri backend may not be available for integration tests');
    }
  });

  describe('Basic Parsing', () => {
    it('should parse complex markdown document', async () => {
      const result = await markdownService.parseMarkdown(complexMarkdown);

      expect(result).toBeDefined();
      expect(result.root.node_type).toBe(MarkdownNodeType.Document);
      expect(result.source_text).toBe(complexMarkdown);
      expect(result.statistics.total_nodes).toBeGreaterThan(50);
      expect(result.statistics.error_nodes).toBe(0);
      expect(result.statistics.parse_time_ms).toBeGreaterThan(0);
      expect(result.statistics.document_length).toBe(complexMarkdown.length);
    });

    it('should handle empty document', async () => {
      const result = await markdownService.parseMarkdown('');
      
      expect(result).toBeDefined();
      expect(result.source_text).toBe('');
      expect(result.statistics.document_length).toBe(0);
    });

    it('should handle malformed markdown gracefully', async () => {
      const result = await markdownService.parseMarkdown(malformedMarkdown);
      
      expect(result).toBeDefined();
      expect(result.root.node_type).toBe(MarkdownNodeType.Document);
      // Should still parse successfully even with errors
      expect(result.statistics.total_nodes).toBeGreaterThan(0);
    });
  });

  describe('Outline Extraction', () => {
    it('should extract complete outline', async () => {
      const outline = await markdownService.extractOutline(complexMarkdown);

      expect(outline).toBeDefined();
      expect(outline.length).toBeGreaterThan(5);

      // Check heading levels
      const levels = outline.map(item => item.level);
      expect(levels).toContain(1); // H1
      expect(levels).toContain(2); // H2
      expect(levels).toContain(3); // H3
      expect(levels).toContain(4); // H4
      expect(levels).toContain(5); // H5
      expect(levels).toContain(6); // H6

      // Check first heading
      expect(outline[0].title).toContain('主标题');
      expect(outline[0].level).toBe(1);
      expect(outline[0].range.start.line).toBe(1); // Adjusted for actual position
    });

    it('should handle document without headings', async () => {
      const noHeadingsMarkdown = 'Just some plain text without any headings.';
      const outline = await markdownService.extractOutline(noHeadingsMarkdown);

      expect(outline).toBeDefined();
      expect(outline.length).toBe(0);
    });
  });

  describe('Link Extraction', () => {
    it('should extract all links and images', async () => {
      const links = await markdownService.extractLinks(complexMarkdown);

      expect(links).toBeDefined();
      expect(links.length).toBeGreaterThanOrEqual(3);

      // Find specific links
      const googleLink = links.find(link => link.url === 'https://google.com');
      expect(googleLink).toBeDefined();
      expect(googleLink?.text).toBe('Google');
      expect(googleLink?.title).toBe('Google搜索');
      expect(googleLink?.link_type).toBe(LinkType.Link);

      const image = links.find(link => link.link_type === LinkType.Image);
      expect(image).toBeDefined();
      expect(image?.url).toContain('image.png');
    });

    it('should handle document without links', async () => {
      const noLinksMarkdown = '# Title\n\nJust plain text.';
      const links = await markdownService.extractLinks(noLinksMarkdown);

      expect(links).toBeDefined();
      expect(links.length).toBe(0);
    });
  });

  describe('Node Queries', () => {
    it('should query headings', async () => {
      const headings = await markdownService.queryHeadings(complexMarkdown);

      expect(headings).toBeDefined();
      expect(headings.length).toBeGreaterThan(5);
      
      headings.forEach(heading => {
        expect(heading.node_type).toBe(MarkdownNodeType.Heading);
        expect(heading.attributes.level).toBeDefined();
      });
    });

    it('should query links', async () => {
      const links = await markdownService.queryLinkNodes(complexMarkdown);

      expect(links).toBeDefined();
      expect(links.length).toBeGreaterThan(0);
      
      links.forEach(link => {
        expect([MarkdownNodeType.Link, MarkdownNodeType.Image]).toContain(link.node_type);
      });
    });

    it('should query code blocks', async () => {
      const codeBlocks = await markdownService.queryCodeBlocks(complexMarkdown);

      expect(codeBlocks).toBeDefined();
      expect(codeBlocks.length).toBeGreaterThanOrEqual(2);
      
      codeBlocks.forEach(block => {
        expect(block.node_type).toBe(MarkdownNodeType.CodeBlock);
      });

      // Check for specific languages
      const jsBlock = codeBlocks.find(block => 
        block.attributes.language === 'javascript'
      );
      expect(jsBlock).toBeDefined();

      const pythonBlock = codeBlocks.find(block => 
        block.attributes.language === 'python'
      );
      expect(pythonBlock).toBeDefined();
    });
  });

  describe('Position Finding', () => {
    it('should find node at specific position', async () => {
      // Find node at the beginning of the document
      const node = await markdownService.findNodeAtPosition(complexMarkdown, 1, 0);

      expect(node).toBeDefined();
      expect(node?.node_type).toBe(MarkdownNodeType.Heading);
    });

    it('should return null for invalid position', async () => {
      const node = await markdownService.findNodeAtPosition(complexMarkdown, 1000, 1000);
      expect(node).toBeNull();
    });
  });

  describe('Document Validation', () => {
    it('should validate well-formed document', async () => {
      const validation = await markdownService.validateMarkdown(complexMarkdown);

      expect(validation).toBeDefined();
      expect(validation.statistics).toBeDefined();
      
      // The complex document might have some warnings but should be mostly valid
      const errorCount = validation.issues.filter(
        issue => issue.severity === ValidationSeverity.Error
      ).length;
      expect(errorCount).toBeLessThanOrEqual(1);
    });

    it('should detect validation issues in malformed document', async () => {
      const validation = await markdownService.validateMarkdown(malformedMarkdown);

      expect(validation).toBeDefined();
      expect(validation.is_valid).toBe(false);
      expect(validation.issues.length).toBeGreaterThan(0);

      // Should detect skipped heading level
      const headingIssue = validation.issues.find(
        issue => issue.issue_type === 'SkippedHeadingLevel'
      );
      expect(headingIssue).toBeDefined();

      // Should detect empty link
      const linkIssue = validation.issues.find(
        issue => issue.issue_type === 'EmptyLink'
      );
      expect(linkIssue).toBeDefined();
    });
  });

  describe('Utility Functions', () => {
    it('should extract text content correctly', async () => {
      const result = await markdownService.parseMarkdown('# **Bold** Title');
      const textContent = markdownService.extractTextContent(result.root);
      
      expect(textContent).toContain('Bold');
      expect(textContent).toContain('Title');
    });

    it('should check node range correctly', async () => {
      const result = await markdownService.parseMarkdown('# Title\n\nParagraph');
      const firstChild = result.root.children[0];
      
      const inRange = markdownService.isNodeInRange(firstChild, 0, 2);
      expect(inRange).toBe(true);
      
      const outOfRange = markdownService.isNodeInRange(firstChild, 5, 10);
      expect(outOfRange).toBe(false);
    });

    it('should format positions correctly', () => {
      const position = markdownService.formatPosition(0, 0);
      expect(position).toBe('1:1'); // 1-based indexing
      
      const range = markdownService.formatRange(
        { line: 0, column: 0 },
        { line: 1, column: 5 }
      );
      expect(range).toBe('1:1-2:6');
    });

    it('should find nodes containing text', async () => {
      const result = await markdownService.parseMarkdown('# Hello\n\nWorld hello');
      const nodes = markdownService.findNodesContainingText([result.root], 'hello');
      
      expect(nodes.length).toBeGreaterThan(0);
    });
  });

  describe('Performance Tests', () => {
    it('should handle large documents efficiently', async () => {
      // Generate a large document
      let largeMarkdown = '# Large Document\n\n';
      for (let i = 0; i < 1000; i++) {
        largeMarkdown += `## Section ${i}\n\nContent for section ${i}.\n\n`;
      }

      const startTime = Date.now();
      const result = await markdownService.parseMarkdown(largeMarkdown);
      const endTime = Date.now();

      expect(result).toBeDefined();
      expect(result.statistics.total_nodes).toBeGreaterThan(3000);
      
      // Should complete within reasonable time (adjust as needed)
      const parseTime = endTime - startTime;
      expect(parseTime).toBeLessThan(5000); // 5 seconds max
    });

    it('should handle deeply nested structures', async () => {
      let nestedMarkdown = '';
      for (let i = 1; i <= 6; i++) {
        nestedMarkdown += '#'.repeat(i) + ` Level ${i} Heading\n\n`;
        nestedMarkdown += `Content for level ${i}.\n\n`;
      }

      const result = await markdownService.parseMarkdown(nestedMarkdown);
      expect(result).toBeDefined();
      expect(result.statistics.max_depth).toBeGreaterThan(3);
    });
  });

  describe('Error Handling', () => {
    it('should handle extremely large documents gracefully', async () => {
      // Create a document that might exceed limits
      const hugeContent = 'a'.repeat(20_000_000); // 20MB of text
      const hugeMarkdown = `# Title\n\n${hugeContent}`;

      try {
        await markdownService.parseMarkdown(hugeMarkdown);
        // If it succeeds, that's fine
      } catch (error) {
        // Should fail gracefully with a meaningful error
        expect(error).toBeInstanceOf(Error);
        expect((error as Error).message).toContain('too large');
      }
    });

    it('should handle invalid UTF-8 gracefully', async () => {
      // This test might need adjustment based on how the backend handles encoding
      const invalidMarkdown = '# Title\n\nSome text with special chars: 🚀 📝 ✨';
      
      const result = await markdownService.parseMarkdown(invalidMarkdown);
      expect(result).toBeDefined();
      expect(result.source_text).toBe(invalidMarkdown);
    });
  });
});

// Helper function to run integration tests only when backend is available
export const runIntegrationTests = async () => {
  try {
    await markdownService.parseMarkdown('# Test');
    return true;
  } catch (error) {
    console.warn('Skipping integration tests - Tauri backend not available');
    return false;
  }
};
