#!/usr/bin/env python3
"""
Generate a Markdown todo list for achieving 100% test coverage.

Parses the coverage report from `./scripts/coverage.sh` and creates a todo list
for files that need additional testing to reach 100% coverage.

Excludes:
- Files in bin/ directory
- main.rs
- Files ending with _test.rs, _tests.rs, test.rs, or tests.rs
"""

import json
import re
import subprocess
import sys
from pathlib import Path
from collections import defaultdict


def should_ignore_file(filename):
    """Check if a file should be ignored based on the exclusion rules."""
    # Ignore bin/ directory
    if filename.startswith('bin/'):
        return True
    
    # Ignore main.rs
    if filename == 'main.rs':
        return True
    
    # Ignore test files
    test_patterns = [
        r'_test\.rs$',
        r'_tests\.rs$', 
        r'/test\.rs$',
        r'/tests\.rs$',
        r'^test\.rs$',
        r'^tests\.rs$'
    ]
    
    for pattern in test_patterns:
        if re.search(pattern, filename):
            return True
    
    return False


def parse_coverage_json(json_file):
    """Parse coverage.json and extract detailed coverage information."""
    with open(json_file, 'r') as f:
        coverage_data = json.load(f)
    
    files_data = {}
    
    # Extract file coverage data
    for data_entry in coverage_data.get('data', []):
        for file_info in data_entry.get('files', []):
            filename = file_info['filename']
            
            # Convert absolute path to relative
            if '/src/' in filename:
                filename = filename.split('/src/')[-1]
            
            # Skip ignored files
            if should_ignore_file(filename):
                continue
                
            summary = file_info['summary']
            lines_summary = summary['lines']
            
            # Get coverage percentage
            coverage_percent = lines_summary.get('percent', 0.0)
            
            # Extract uncovered line numbers from segments
            uncovered_lines = set()
            segments = file_info.get('segments', [])
            
            for segment in segments:
                if len(segment) >= 4:
                    line_num = segment[0]  # Line number
                    is_covered = segment[3]  # Coverage flag
                    
                    if not is_covered:
                        uncovered_lines.add(line_num)
            
            if coverage_percent < 100.0:
                files_data[filename] = {
                    'coverage': coverage_percent,
                    'uncovered_lines': sorted(uncovered_lines),
                    'total_lines': lines_summary.get('count', 0),
                    'covered_lines': lines_summary.get('covered', 0)
                }
    
    return files_data


def generate_coverage_todo():
    """Generate the coverage todo list."""
    # Check if coverage data already exists, otherwise run the script
    json_file = Path('coverage/coverage.json')
    if not json_file.exists():
        print("Coverage data not found. Running coverage script...")
        try:
            result = subprocess.run(['./scripts/coverage.sh'], 
                                  capture_output=True, text=True, cwd='.')
            if result.returncode != 0:
                print(f"Error running coverage script: {result.stderr}")
                sys.exit(1)
        except FileNotFoundError:
            print("Error: ./scripts/coverage.sh not found. Make sure you're in the project root.")
            sys.exit(1)
    else:
        print("Using existing coverage data from coverage/coverage.json")
    
    # Parse detailed coverage data from JSON
    files_data = parse_coverage_json(json_file)
    
    # Sort by coverage percentage (lowest first)
    files_needing_tests = [(filename, data) for filename, data in files_data.items()]
    files_needing_tests.sort(key=lambda x: x[1]['coverage'])
    
    # Generate Markdown content
    markdown_content = []
    markdown_content.append("# Test Coverage TODO\n")
    markdown_content.append("Files that need additional tests to reach 100% coverage.\n")
    markdown_content.append(f"Generated from coverage report. Found {len(files_needing_tests)} files needing tests.\n")
    markdown_content.append("")
    
    for filename, data in files_needing_tests:
        coverage = data['coverage']
        uncovered_lines = data['uncovered_lines']
        total_lines = data['total_lines']
        covered_lines = data['covered_lines']
        
        markdown_content.append(f"## {filename}")
        markdown_content.append(f"Current coverage: {coverage:.2f}% ({covered_lines}/{total_lines} lines)")
        markdown_content.append(f"Missing: {100 - coverage:.2f}% coverage")
        markdown_content.append("")
        
        if uncovered_lines:
            markdown_content.append("**Uncovered lines:**")
            # Group consecutive line numbers for cleaner display
            line_ranges = []
            current_range = [uncovered_lines[0]]
            
            for line in uncovered_lines[1:]:
                if line == current_range[-1] + 1:
                    current_range.append(line)
                else:
                    line_ranges.append(current_range)
                    current_range = [line]
            line_ranges.append(current_range)
            
            # Format line ranges
            range_strs = []
            for line_range in line_ranges:
                if len(line_range) == 1:
                    range_strs.append(f"{line_range[0]}")
                elif len(line_range) == 2:
                    range_strs.append(f"{line_range[0]}, {line_range[1]}")
                else:
                    range_strs.append(f"{line_range[0]}-{line_range[-1]}")
            
            markdown_content.append(f"Lines: {', '.join(range_strs)}")
            markdown_content.append("")
        
        markdown_content.append("**TODO:**")
        if uncovered_lines:
            markdown_content.append(f"- [ ] Test uncovered lines: {', '.join(map(str, uncovered_lines[:10]))}{'...' if len(uncovered_lines) > 10 else ''}")
            markdown_content.append("- [ ] Add unit tests for missing code paths")
            markdown_content.append("- [ ] Add integration tests if needed")
        else:
            markdown_content.append("- [ ] Investigate why coverage is not 100% despite no uncovered lines")
        markdown_content.append("- [ ] Verify 100% coverage achieved")
        markdown_content.append("")
    
    if not files_needing_tests:
        markdown_content.append("🎉 **All files have 100% coverage!**")
        markdown_content.append("")
    
    # Write to output file
    output_file = Path('reports/TEST_COVERAGE_TODO.md')
    with open(output_file, 'w') as f:
        f.write('\n'.join(markdown_content))
    
    print(f"Coverage todo list generated: {output_file}")
    print(f"Found {len(files_needing_tests)} files needing additional tests")
    
    # Print summary
    if files_needing_tests:
        print("\nFiles with lowest coverage:")
        for filename, data in files_needing_tests[:5]:  # Show top 5
            coverage = data['coverage']
            uncovered_count = len(data['uncovered_lines'])
            print(f"  {filename}: {coverage:.2f}% ({uncovered_count} uncovered lines)")


if __name__ == '__main__':
    generate_coverage_todo()