import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { EnhancedMarkdownRenderer } from '../../components/EnhancedMarkdownRenderer';
import { markdownService } from '../../services/markdownService';
import {
  MarkdownParseResult,
  MarkdownNode,
  MarkdownNodeType,
  ValidationResult,
} from '../../types/markdown';

// Mock the markdown service
vi.mock('../../services/markdownService', () => ({
  markdownService: {
    parseMarkdown: vi.fn(),
    validateMarkdown: vi.fn(),
    extractTextContent: vi.fn(),
  },
}));

const mockMarkdownService = markdownService as any;

describe('EnhancedMarkdownRenderer', () => {
  const mockParseResult: MarkdownParseResult = {
    root: {
      node_type: MarkdownNodeType.Document,
      content: '# Test\n\nHello **world**',
      range: {
        start: { line: 0, column: 0, offset: 0 },
        end: { line: 2, column: 12, offset: 19 },
      },
      children: [
        {
          node_type: MarkdownNodeType.Heading,
          content: '# Test',
          range: {
            start: { line: 0, column: 0, offset: 0 },
            end: { line: 0, column: 6, offset: 6 },
          },
          children: [
            {
              node_type: MarkdownNodeType.Text,
              content: 'Test',
              range: {
                start: { line: 0, column: 2, offset: 2 },
                end: { line: 0, column: 6, offset: 6 },
              },
              children: [],
              attributes: {},
            },
          ],
          attributes: { level: '1' },
        },
        {
          node_type: MarkdownNodeType.Paragraph,
          content: 'Hello **world**',
          range: {
            start: { line: 2, column: 0, offset: 8 },
            end: { line: 2, column: 15, offset: 23 },
          },
          children: [
            {
              node_type: MarkdownNodeType.Text,
              content: 'Hello ',
              range: {
                start: { line: 2, column: 0, offset: 8 },
                end: { line: 2, column: 6, offset: 14 },
              },
              children: [],
              attributes: {},
            },
            {
              node_type: MarkdownNodeType.Strong,
              content: '**world**',
              range: {
                start: { line: 2, column: 6, offset: 14 },
                end: { line: 2, column: 15, offset: 23 },
              },
              children: [
                {
                  node_type: MarkdownNodeType.Text,
                  content: 'world',
                  range: {
                    start: { line: 2, column: 8, offset: 16 },
                    end: { line: 2, column: 13, offset: 21 },
                  },
                  children: [],
                  attributes: {},
                },
              ],
              attributes: {},
            },
          ],
          attributes: {},
        },
      ],
      attributes: {},
    },
    statistics: {
      total_nodes: 5,
      error_nodes: 0,
      parse_time_ms: 3,
      document_length: 19,
      max_depth: 3,
    },
    source_text: '# Test\n\nHello **world**',
  };

  const mockValidation: ValidationResult = {
    is_valid: true,
    issues: [],
    statistics: {
      total_nodes: 5,
      error_nodes: 0,
      parse_time_ms: 3,
      document_length: 19,
      max_depth: 3,
    },
  };

  beforeEach(() => {
    mockMarkdownService.parseMarkdown.mockResolvedValue(mockParseResult);
    mockMarkdownService.validateMarkdown.mockResolvedValue(mockValidation);
    mockMarkdownService.extractTextContent.mockImplementation((node: MarkdownNode) => {
      if (node.node_type === MarkdownNodeType.Text) {
        return node.content;
      }
      return node.children.map(child => mockMarkdownService.extractTextContent(child)).join('');
    });
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it('renders markdown content using MarkdownService', async () => {
    render(
      <EnhancedMarkdownRenderer 
        content="# Test\n\nHello **world**" 
        enableMarkdown={true}
      />
    );

    await waitFor(() => {
      expect(mockMarkdownService.parseMarkdown).toHaveBeenCalledWith(expect.stringContaining('# Test'));
    });

    await waitFor(() => {
      expect(screen.getByText('Test')).toBeInTheDocument();
      expect(screen.getByText('Hello')).toBeInTheDocument();
      expect(screen.getByText('world')).toBeInTheDocument();
    });
  });

  it('renders plain text when markdown is disabled', () => {
    render(
      <EnhancedMarkdownRenderer 
        content="# Test\n\nHello **world**" 
        enableMarkdown={false}
      />
    );

    expect(screen.getByText(/# Test.*Hello \*\*world\*\*/s)).toBeInTheDocument();
    expect(mockMarkdownService.parseMarkdown).not.toHaveBeenCalled();
  });

  it('shows loading state during parsing', () => {
    // Make parseMarkdown return a pending promise
    mockMarkdownService.parseMarkdown.mockReturnValue(new Promise(() => {}));

    render(
      <EnhancedMarkdownRenderer 
        content="# Test" 
        enableMarkdown={true}
      />
    );

    expect(screen.getByText('解析中...')).toBeInTheDocument();
  });

  it('shows error state when parsing fails', async () => {
    mockMarkdownService.parseMarkdown.mockRejectedValue(new Error('Parse error'));

    render(
      <EnhancedMarkdownRenderer 
        content="# Test" 
        enableMarkdown={true}
      />
    );

    await waitFor(() => {
      expect(screen.getByText('解析错误')).toBeInTheDocument();
      expect(screen.getByText('Parse error')).toBeInTheDocument();
    });
  });

  it('shows statistics when enabled', async () => {
    render(
      <EnhancedMarkdownRenderer 
        content="# Test" 
        enableMarkdown={true}
        showStatistics={true}
      />
    );

    await waitFor(() => {
      expect(screen.getByText('解析统计')).toBeInTheDocument();
      expect(screen.getByText('节点数: 5')).toBeInTheDocument();
      expect(screen.getByText('解析时间: 3ms')).toBeInTheDocument();
    });
  });

  it('shows validation warnings when document is invalid', async () => {
    const invalidValidation: ValidationResult = {
      is_valid: false,
      issues: [
        {
          issue_type: 'SkippedHeadingLevel' as any,
          message: 'Heading level jumps from 1 to 3',
          range: {
            start: { line: 2, column: 0, offset: 10 },
            end: { line: 2, column: 10, offset: 20 },
          },
          severity: 'Warning' as any,
        },
      ],
      statistics: mockValidation.statistics,
    };

    mockMarkdownService.validateMarkdown.mockResolvedValue(invalidValidation);

    render(
      <EnhancedMarkdownRenderer 
        content="# Test\n\n### Skipped" 
        enableMarkdown={true}
      />
    );

    await waitFor(() => {
      expect(screen.getByText('文档验证警告')).toBeInTheDocument();
      expect(screen.getByText('• Heading level jumps from 1 to 3')).toBeInTheDocument();
    });
  });

  it('renders grounding metadata when provided', async () => {
    const groundingMetadata = {
      sources: [
        { title: 'Source 1', url: 'https://example.com/1' },
        { title: 'Source 2', url: 'https://example.com/2' },
      ],
    };

    render(
      <EnhancedMarkdownRenderer 
        content="# Test" 
        enableMarkdown={true}
        enableReferences={true}
        groundingMetadata={groundingMetadata}
      />
    );

    await waitFor(() => {
      expect(screen.getByText('基于 2 个来源的信息')).toBeInTheDocument();
      expect(screen.getByText('1')).toBeInTheDocument();
      expect(screen.getByText('2')).toBeInTheDocument();
    });
  });

  it('handles real-time parsing when enabled', async () => {
    const { rerender } = render(
      <EnhancedMarkdownRenderer 
        content="# Test" 
        enableMarkdown={true}
        enableRealTimeParsing={true}
      />
    );

    // Change content
    rerender(
      <EnhancedMarkdownRenderer 
        content="# Updated Test" 
        enableMarkdown={true}
        enableRealTimeParsing={true}
      />
    );

    // Wait for debounced parsing
    await waitFor(() => {
      expect(mockMarkdownService.parseMarkdown).toHaveBeenCalledWith('# Updated Test');
    }, { timeout: 1000 });
  });

  it('applies custom className', () => {
    const { container } = render(
      <EnhancedMarkdownRenderer 
        content="# Test" 
        enableMarkdown={true}
        className="custom-class"
      />
    );

    expect(container.firstChild).toHaveClass('custom-class');
  });

  it('renders different heading levels correctly', async () => {
    const headingResult: MarkdownParseResult = {
      ...mockParseResult,
      root: {
        ...mockParseResult.root,
        children: [
          {
            node_type: MarkdownNodeType.Heading,
            content: '# H1',
            range: { start: { line: 0, column: 0, offset: 0 }, end: { line: 0, column: 4, offset: 4 } },
            children: [{ node_type: MarkdownNodeType.Text, content: 'H1', range: { start: { line: 0, column: 2, offset: 2 }, end: { line: 0, column: 4, offset: 4 } }, children: [], attributes: {} }],
            attributes: { level: '1' },
          },
          {
            node_type: MarkdownNodeType.Heading,
            content: '## H2',
            range: { start: { line: 1, column: 0, offset: 5 }, end: { line: 1, column: 5, offset: 10 } },
            children: [{ node_type: MarkdownNodeType.Text, content: 'H2', range: { start: { line: 1, column: 3, offset: 8 }, end: { line: 1, column: 5, offset: 10 } }, children: [], attributes: {} }],
            attributes: { level: '2' },
          },
        ],
      },
    };

    mockMarkdownService.parseMarkdown.mockResolvedValue(headingResult);

    render(
      <EnhancedMarkdownRenderer 
        content="# H1\n## H2" 
        enableMarkdown={true}
      />
    );

    await waitFor(() => {
      const h1 = screen.getByRole('heading', { level: 1 });
      const h2 = screen.getByRole('heading', { level: 2 });
      
      expect(h1.textContent).toBe('H1');
      expect(h2.textContent).toBe('H2');
    });
  });
});
