#!/usr/bin/env python3

"""
Validate Debtmap Improvement Command
Compares debtmap JSON output before and after changes to validate improvements
"""

import json
import sys
import os
import argparse
from pathlib import Path
from typing import Dict, List, Any, Tuple

def parse_arguments():
    """Parse command line arguments."""
    parser = argparse.ArgumentParser(description='Validate debtmap improvements')
    parser.add_argument('--before', required=True, help='Path to before JSON file')
    parser.add_argument('--after', required=True, help='Path to after JSON file')
    parser.add_argument('--output', default='.prodigy/debtmap-validation.json',
                       help='Output file path')
    return parser.parse_args()

def load_json_file(filepath: str) -> Dict[str, Any]:
    """Load and parse a JSON file."""
    try:
        with open(filepath, 'r') as f:
            return json.load(f)
    except (json.JSONDecodeError, FileNotFoundError) as e:
        print(f"Error loading {filepath}: {e}", file=sys.stderr)
        return {}

def extract_items(analysis: Dict[str, Any]) -> List[Dict[str, Any]]:
    """Extract debt items from the analysis."""
    items = []

    # Try UnifiedAnalysis structure
    if 'items' in analysis and isinstance(analysis['items'], list):
        items.extend(analysis['items'])

    # Also check for file_items
    if 'file_items' in analysis and isinstance(analysis['file_items'], list):
        items.extend(analysis['file_items'])

    # Check for file_aggregates
    if 'file_aggregates' in analysis and isinstance(analysis['file_aggregates'], list):
        items.extend(analysis['file_aggregates'])

    return items

def count_high_priority(items: List[Dict[str, Any]]) -> int:
    """Count high-priority debt items (score >= 8.0)."""
    count = 0
    for item in items:
        score = 0.0

        # Check unified_score.final_score
        if 'unified_score' in item and 'final_score' in item['unified_score']:
            score = float(item['unified_score']['final_score'])
        # Check score field
        elif 'score' in item:
            score = float(item['score'])
        # Check priority field
        elif 'priority' in item:
            priority = item['priority']
            if priority in ['Critical', 'High']:
                count += 1
                continue

        if score >= 8.0:
            count += 1

    return count

def calculate_average_score(items: List[Dict[str, Any]]) -> float:
    """Calculate average debt score."""
    if not items:
        return 0.0

    scores = []
    for item in items:
        score = None

        # Try unified_score.final_score
        if 'unified_score' in item and 'final_score' in item['unified_score']:
            score = float(item['unified_score']['final_score'])
        # Try score field
        elif 'score' in item:
            score = float(item['score'])

        if score is not None:
            scores.append(score)

    return sum(scores) / len(scores) if scores else 0.0

def check_complexity_improvements(before: Dict[str, Any], after: Dict[str, Any]) -> int:
    """Count functions with reduced complexity."""
    improved = 0

    # Build map of before complexities
    before_complexities = {}
    if 'complexity' in before and 'metrics' in before['complexity']:
        for metric in before['complexity']['metrics']:
            if 'name' in metric and 'cyclomatic' in metric:
                before_complexities[metric['name']] = metric['cyclomatic']

    # Check for improvements
    if 'complexity' in after and 'metrics' in after['complexity']:
        for metric in after['complexity']['metrics']:
            if 'name' in metric and 'cyclomatic' in metric:
                name = metric['name']
                if name in before_complexities:
                    if metric['cyclomatic'] < before_complexities[name]:
                        improved += 1

    return improved

def find_critical_issues(items: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
    """Find remaining critical issues."""
    critical = []
    for item in items:
        score = 0.0

        # Check unified_score.final_score
        if 'unified_score' in item and 'final_score' in item['unified_score']:
            score = float(item['unified_score']['final_score'])
        # Check score field
        elif 'score' in item:
            score = float(item['score'])

        if score >= 8.0:
            critical.append(item)

    return critical

def extract_location(item: Dict[str, Any]) -> str:
    """Extract location string from an item."""
    # Try location object
    if 'location' in item:
        loc = item['location']
        if isinstance(loc, dict):
            if 'file' in loc and 'line' in loc:
                return f"{loc['file']}:{loc['line']}"
            elif 'file' in loc:
                return loc['file']
        elif isinstance(loc, str):
            return loc

    # Try file and line directly
    if 'file' in item and 'line' in item:
        return f"{item['file']}:{item['line']}"
    elif 'file' in item:
        return item['file']

    return "unknown"

def check_new_debt(before: Dict[str, Any], after: Dict[str, Any]) -> int:
    """Check for new debt items introduced."""
    before_locations = set()

    # Collect all before locations
    before_items = extract_items(before)
    for item in before_items:
        before_locations.add(extract_location(item))

    # Count new high-priority items
    new_count = 0
    after_items = extract_items(after)
    for item in after_items:
        location = extract_location(item)
        if location not in before_locations and location != "unknown":
            # Check if it's high priority
            score = 0.0
            if 'unified_score' in item and 'final_score' in item['unified_score']:
                score = float(item['unified_score']['final_score'])
            elif 'score' in item:
                score = float(item['score'])

            if score >= 6.0:
                new_count += 1

    return new_count

def calculate_completion_score(before_high: int, after_high: int,
                              before_avg: float, after_avg: float,
                              no_new_critical: bool,
                              complexity_improvements: int) -> float:
    """Calculate overall completion percentage."""
    score = 0.0

    # 40% weight: High-priority items resolved
    if before_high > 0:
        high_priority_resolved = max(0, before_high - after_high) / before_high
        score += high_priority_resolved * 40.0
    else:
        score += 40.0  # No high-priority items to begin with

    # 30% weight: Overall score improvement
    if before_avg > 0.0:
        score_improvement = max(0.0, before_avg - after_avg) / before_avg
        score += score_improvement * 30.0
    else:
        score += 30.0  # No score to improve

    # 20% weight: Complexity reduction
    if complexity_improvements > 0:
        # Give full points if at least 2 functions improved
        complexity_score = min(1.0, complexity_improvements / 2.0)
        score += complexity_score * 20.0

    # 10% weight: No new critical debt
    if no_new_critical:
        score += 10.0

    return score

def analyze_improvement(before: Dict[str, Any], after: Dict[str, Any]) -> Dict[str, Any]:
    """Analyze the improvement between before and after states."""
    improvements = []
    remaining_issues = []
    gaps = {}

    # Extract metrics
    before_items = extract_items(before)
    after_items = extract_items(after)

    before_high_priority = count_high_priority(before_items)
    after_high_priority = count_high_priority(after_items)

    before_total = len(before_items)
    after_total = len(after_items)

    before_avg_score = calculate_average_score(before_items)
    after_avg_score = calculate_average_score(after_items)

    # Track improvements
    if after_total < before_total:
        improvements.append(f"Resolved {before_total - after_total} debt items")

    if after_high_priority < before_high_priority:
        improvements.append(f"Resolved {before_high_priority - after_high_priority} high-priority debt items")

    if after_avg_score < before_avg_score and before_avg_score > 0:
        reduction = int((before_avg_score - after_avg_score) / before_avg_score * 100)
        improvements.append(f"Reduced average debt score by {reduction}%")

    # Check complexity improvements
    complexity_improved = check_complexity_improvements(before, after)
    if complexity_improved > 0:
        improvements.append(f"Reduced complexity in {complexity_improved} functions")

    # Find remaining critical issues
    critical_remaining = find_critical_issues(after_items)
    if critical_remaining:
        remaining_issues.append(f"{len(critical_remaining)} critical debt items still present")

        # Add first few critical items to gaps
        for i, issue in enumerate(critical_remaining[:3]):
            location = extract_location(issue)
            gap_key = f"critical_debt_{i + 1}"

            message = "High-priority debt item unresolved"
            if 'message' in issue:
                message = issue['message']
            elif 'description' in issue:
                message = issue['description']

            score = None
            if 'unified_score' in issue and 'final_score' in issue['unified_score']:
                score = issue['unified_score']['final_score']
            elif 'score' in issue:
                score = issue['score']

            gaps[gap_key] = {
                "description": message,
                "location": location,
                "severity": "critical",
                "suggested_fix": "Apply functional programming patterns to reduce complexity",
                "original_score": score,
                "current_score": score
            }

    # Check for new debt
    new_debt_count = check_new_debt(before, after)
    if new_debt_count > 0:
        remaining_issues.append(f"{new_debt_count} new debt items introduced")
        gaps["regression_detected"] = {
            "description": "New technical debt introduced during refactoring",
            "location": "Multiple locations",
            "severity": "high",
            "suggested_fix": "Review recent changes and simplify new code",
            "current_score": 7.8
        }

    # Calculate completion percentage
    completion_percentage = calculate_completion_score(
        before_high_priority, after_high_priority,
        before_avg_score, after_avg_score,
        new_debt_count == 0,
        complexity_improved
    )

    status = "complete" if completion_percentage >= 75.0 else "incomplete"

    return {
        "completion_percentage": round(completion_percentage, 1),
        "status": status,
        "improvements": improvements,
        "remaining_issues": remaining_issues,
        "gaps": gaps,
        "before_summary": {
            "total_items": before_total,
            "high_priority_items": before_high_priority,
            "average_score": round(before_avg_score, 2)
        },
        "after_summary": {
            "total_items": after_total,
            "high_priority_items": after_high_priority,
            "average_score": round(after_avg_score, 2)
        }
    }

def main():
    """Main entry point."""
    args = parse_arguments()

    # Check if files exist
    if not os.path.exists(args.before):
        print(f"Error: Before file does not exist: {args.before}", file=sys.stderr)
        sys.exit(1)

    if not os.path.exists(args.after):
        print(f"Error: After file does not exist: {args.after}", file=sys.stderr)
        sys.exit(1)

    # Detect automation mode
    automation_mode = os.environ.get('PRODIGY_AUTOMATION') == 'true' or \
                     os.environ.get('PRODIGY_VALIDATION') == 'true'

    if not automation_mode:
        print("Loading debtmap states for comparison...")

    # Create output directory if needed
    output_dir = os.path.dirname(args.output)
    if output_dir:
        os.makedirs(output_dir, exist_ok=True)

    try:
        # Load JSON files
        before = load_json_file(args.before)
        after = load_json_file(args.after)

        # Analyze improvement
        result = analyze_improvement(before, after)

        # Write result to output file
        with open(args.output, 'w') as f:
            json.dump(result, f, indent=2)

        if not automation_mode:
            print(f"Validation complete. Results written to: {args.output}")

        # Exit with 0 regardless of validation result
        sys.exit(0)

    except Exception as e:
        # On any error, write a failure result
        error_result = {
            "completion_percentage": 0.0,
            "status": "failed",
            "improvements": [],
            "remaining_issues": [f"Analysis failed: {str(e)}"],
            "gaps": {},
            "raw_output": str(e),
            "before_summary": {
                "total_items": 0,
                "high_priority_items": 0,
                "average_score": 0.0
            },
            "after_summary": {
                "total_items": 0,
                "high_priority_items": 0,
                "average_score": 0.0
            }
        }

        with open(args.output, 'w') as f:
            json.dump(error_result, f, indent=2)

        sys.exit(0)

if __name__ == '__main__':
    main()