#!/usr/bin/env python3
"""
Enhanced Devour Scorecard Generator - Extended version with more visual elements.
Enhanced data visualization with additional metrics and improved layout.
"""

from __future__ import annotations

import json
import logging
import math
import os
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Tuple, Any, Optional

try:
    from PIL import Image, ImageDraw, ImageFont
except ImportError:
    print("Error: PIL/Pillow required. Install with: pip install Pillow")
    sys.exit(1)

logger = logging.getLogger(__name__)

# Enhanced visual constants
SCALE = 2
BG = (248, 248, 246)
FRAME = (222, 222, 220)
BORDER = (200, 200, 198)
ACCENT = (88, 166, 255)
TEXT = (40, 44, 52)
DIM = (140, 140, 140)
BG_SCORE = (255, 255, 255)
BG_TABLE = (255, 255, 255)
BG_ROW_ALT = (250, 250, 248)
BG_GRADIENT_START = (240, 240, 238)
BG_GRADIENT_END = (248, 248, 246)

# Extended color palette
COLORS = {
    'excellent': (68, 120, 68),      # deep sage
    'good': (120, 140, 72),         # olive green  
    'moderate': (145, 155, 80),      # yellow-green
    'poor': (255, 193, 7),          # orange
    'critical': (220, 38, 127),        # red
    'info': (88, 166, 255),            # blue accent
    'warning': (255, 152, 0),          # orange accent
}

@dataclass
class EnhancedScorecardData:
    """Enhanced data structure for comprehensive scorecard."""
    project_name: str
    version: str
    main_score: float
    strict_score: float
    dimensions: List[Tuple[str, Dict[str, Any]]]
    trends: Dict[str, List[float]]
    recommendations: List[str]
    metadata: Dict[str, Any]


def scale(value: int) -> int:
    """Scale value by retina factor."""
    return value * SCALE


def score_to_color(score: float) -> Tuple[int, int, int]:
    """Convert score to color."""
    if score >= 90:
        return COLORS['excellent']
    elif score >= 70:
        return COLORS['good']
    elif score >= 50:
        return COLORS['moderate']
    elif score >= 30:
        return COLORS['poor']
    else:
        return COLORS['critical']


def load_font(size: int, *, bold: bool = False) -> ImageFont.ImageFont:
    """Load font with fallback."""
    size = size * SCALE
    candidates = [
        "/System/Library/Fonts/SFCompact.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
        "arial.ttf"
    ]
    
    if bold:
        candidates = [
            "/System/Library/Fonts/SFCompact.ttf",
            "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
            "arialbd.ttf"
        ]
    
    for path in candidates:
        try:
            if os.path.exists(path):
                return ImageFont.truetype(path, size)
        except OSError:
            continue
    
    return ImageFont.load_default()


def draw_mini_sparkline(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int, height: int,
    values: List[float], color: Tuple[int, int, int]
) -> None:
    """Draw a mini sparkline chart."""
    if len(values) < 2:
        return
    
    # Draw background
    draw.rectangle([x, y, x + width, y + height], fill=BG_TABLE, outline=BORDER)
    
    # Calculate points
    padding = 3
    chart_width = width - 2 * padding
    chart_height = height - 2 * padding
    step_x = chart_width / (len(values) - 1)
    
    points = []
    for i, value in enumerate(values):
        px = x + padding + i * step_x
        py = y + padding + chart_height - (value / 100) * chart_height
        points.append((px, py))
    
    # Draw line
    if len(points) > 1:
        draw.line(points, fill=color, width=2)
    
    # Draw points
    for px, py in points:
        draw.ellipse([px - 2, py - 2, px + 2, py + 2], fill=color, outline=TEXT)


def draw_progress_ring(
    draw: ImageDraw.ImageDraw,
    cx: int, cy: int, outer_radius: int,
    progress: float, color: Tuple[int, int, int],
    label: str = ""
) -> None:
    """Draw a circular progress ring."""
    # Background ring
    draw.ellipse([cx - outer_radius, cy - outer_radius, cx + outer_radius, cy + outer_radius],
                 fill=BG_TABLE, outline=BORDER, width=2)
    
    # Progress arc
    if progress > 0:
        inner_radius = outer_radius - 8
        start_angle = 270
        end_angle = start_angle - (progress / 100) * 360
        
        # Draw multiple arcs for thickness effect
        for i in range(3):
            radius_offset = i * 2
            draw.arc([cx - outer_radius + radius_offset, cy - outer_radius + radius_offset,
                     cx + outer_radius + radius_offset, cy + outer_radius + radius_offset],
                   start=start_angle, end=end_angle, 
                   fill=color, width=6 - i * 2)
    
    # Center text
    if label:
        font = load_font(10, bold=True)
        bbox = draw.textbbox((0, 0), label, font=font)
        text_width = bbox[2] - bbox[0]
        text_height = bbox[3] - bbox[1]
        
        draw.text((cx - text_width // 2, cy - text_height // 2 + bbox[1]), 
                   label, fill=TEXT, font=font)


def draw_trend_indicator(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, size: int,
    trend: str, value: float
) -> None:
    """Draw a trend indicator with arrow."""
    # Background circle
    draw.ellipse([x, y, x + size, y + size], fill=BG_TABLE, outline=BORDER)
    
    # Trend arrow
    cx, cy = x + size // 2, y + size // 2
    arrow_size = size // 4
    
    color = COLORS['good'] if trend == 'up' else COLORS['poor'] if trend == 'down' else COLORS['moderate']
    
    if trend == 'up':
        # Up arrow
        points = [
            (cx, cy + arrow_size),
            (cx - arrow_size // 2, cy),
            (cx + arrow_size // 2, cy)
        ]
    elif trend == 'down':
        # Down arrow
        points = [
            (cx, cy - arrow_size),
            (cx - arrow_size // 2, cy),
            (cx + arrow_size // 2, cy)
        ]
    else:
        # Circle for stable
        draw.ellipse([cx - arrow_size // 2, cy - arrow_size // 2,
                     cx + arrow_size // 2, cy + arrow_size // 2],
                     fill=color, outline=TEXT)
        return
    
    draw.polygon(points, fill=color)
    
    # Value text
    font = load_font(8)
    text = f"{value:.0f}%"
    bbox = draw.textbbox((0, 0), text, font=font)
    text_width = bbox[2] - bbox[0]
    
    draw.text((cx - text_width // 2, y + size + 5), text, fill=TEXT, font=font)


def generate_enhanced_scorecard(data: EnhancedScorecardData, output_path: str | Path) -> Path:
    """Generate enhanced scorecard with additional visual elements."""
    output_path = Path(output_path)
    
    # Layout - larger canvas for more elements
    width = scale(900)
    height = scale(700)
    margin = scale(20)
    
    # Create image with gradient background
    img = Image.new("RGB", (width, height), BG)
    draw = ImageDraw.Draw(img)
    
    # Add subtle gradient overlay
    for i in range(height):
        alpha = i / height
        color = tuple(
            int(BG_GRADIENT_START[j] + alpha * (BG_GRADIENT_END[j] - BG_GRADIENT_START[j]))
            for j in range(3)
        )
        draw.line([(0, i), (width, i)], fill=color)
    
    # Header section with enhanced styling
    header_height = scale(100)
    draw_header_section(draw, margin, data, header_height)
    
    # Main content area
    content_y = margin + header_height + scale(20)
    
    # Left panel - enhanced score display
    left_panel_width = scale(280)
    draw_enhanced_left_panel(draw, margin, content_y, left_panel_width, data)
    
    # Right panel - dimensions with trends
    right_panel_x = margin + left_panel_width + scale(20)
    right_panel_width = width - right_panel_x - margin
    draw_enhanced_right_panel(draw, right_panel_x, content_y, right_panel_width, data)
    
    # Bottom recommendations
    recommendations_y = content_y + scale(200)
    draw_recommendations_section(draw, margin, recommendations_y, width - 2 * margin, data.recommendations)
    
    # Footer with metadata
    footer_y = height - scale(40)
    draw_footer_section(draw, margin, footer_y, width - 2 * margin, data.metadata)
    
    # Save image
    output_path.parent.mkdir(parents=True, exist_ok=True)
    img.save(str(output_path), "PNG", optimize=True)
    return output_path


def draw_header_section(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int,
    data: EnhancedScorecardData
) -> None:
    """Draw enhanced header section."""
    # Project title
    font_title = load_font(18, bold=True)
    title = f"{data.project_name} Code Health"
    title_bbox = draw.textbbox((0, 0), title, font=font_title)
    title_width = title_bbox[2] - title_bbox[0]
    
    # Background panel for title
    panel_height = scale(40)
    draw.rectangle([x, y, x + width, y + panel_height], 
                 fill=BG_SCORE, outline=ACCENT, width=2)
    
    draw.text((x + (width - title_width) // 2, y + scale(12)), 
               title, fill=TEXT, font=font_title)
    
    # Version and timestamp
    font_info = load_font(10)
    timestamp_str = data.metadata.get('timestamp', '')
    timestamp_str = data.metadata.get('timestamp', '')
    info_text = "v" + str(data.version) + " - Generated " + str(data.metadata.get('timestamp', ''))


def draw_enhanced_left_panel(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int,
    data: EnhancedScorecardData
) -> None:
    """Draw enhanced left panel with multiple visual elements."""
    # Main score with large display
    score_y = y + scale(20)
    score_size = scale(80)
    
    # Circular progress indicator
    draw_progress_ring(draw, x + width // 2 - score_size // 2, score_y, score_size // 2,
                       progress=data.main_score, color=score_to_color(data.main_score))
    
    # Score text
    font_score = load_font(36, bold=True)
    score_text = f"{int(data.main_score)}"
    score_bbox = draw.textbbox((0, 0), score_text, font=font_score)
    score_width = score_bbox[2] - score_bbox[0]
    score_height = score_bbox[3] - score_bbox[1]
    
    score_bg_y = score_y + score_size + scale(10)
    score_bg_height = scale(50)
    
    # Background for score text
    draw.rectangle([x, score_bg_y, x + width, score_bg_y + score_bg_height],
                 fill=BG_SCORE, outline=BORDER, width=1)
    
    draw.text((x + (width - score_width) // 2, score_bg_y + score_bg_height // 2 - score_height // 2 + score_bbox[1]), 
               score_text, fill=TEXT, font=font_score)
    
    # Strict score
    strict_y = score_bg_y + score_bg_height + scale(15)
    font_strict = load_font(14)
    strict_text = f"Strict: {int(data.strict_score)}"
    strict_bbox = draw.textbbox((0, 0), strict_text, font=font_strict)
    strict_width = strict_bbox[2] - strict_bbox[0]
    
    draw.text((x + (width - strict_width) // 2, strict_y - strict_bbox[1]), 
               strict_text, fill=score_to_color(data.strict_score, muted=True), font=font_strict)
    
    # Grade indicator
    grade_y = strict_y + scale(30)
    grade_size = scale(40)
    grade = "A" if data.main_score >= 90 else "B" if data.main_score >= 70 else "C" if data.main_score >= 50 else "D" if data.main_score >= 30 else "F"
    
    draw_progress_ring(draw, x + width // 2 - grade_size // 2, grade_y, grade_size // 2,
                       progress=data.main_score, color=score_to_color(data.main_score))
    
    font_grade = load_font(24, bold=True)
    grade_bbox = draw.textbbox((0, 0), grade, font=font_grade)
    grade_width = grade_bbox[2] - grade_bbox[0]
    
    draw.text((x + (width - grade_width) // 2, grade_y + grade_size + scale(10) - grade_bbox[1]), 
               grade, fill=TEXT, font=font_grade)


def draw_enhanced_right_panel(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int,
    data: EnhancedScorecardData
) -> None:
    """Draw enhanced right panel with dimensions and trends."""
    # Dimensions table
    table_y = y + scale(20)
    table_height = scale(120)
    
    draw_enhanced_dimensions_table(draw, x, table_y, width, table_height, data.dimensions)
    
    # Trends section
    trends_y = table_y + table_height + scale(20)
    draw_trends_section(draw, x, trends_y, width, data.trends)


def draw_enhanced_dimensions_table(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int, height: int,
    dimensions: List[Tuple[str, Dict[str, Any]]]
) -> None:
    """Draw enhanced dimensions table with sparklines."""
    font_header = load_font(11, bold=True)
    font_row = load_font(9)
    
    # Table header
    header_y = y
    draw.text((x, header_y), "CATEGORY", fill=TEXT, font=font_header)
    draw.text((x + scale(120), header_y), "SCORE", fill=TEXT, font=font_header)
    draw.text((x + scale(200), header_y), "TREND", fill=TEXT, font=font_header)
    
    # Table rows
    row_y = header_y + scale(20)
    row_height = scale(25)
    
    for i, (name, data) in enumerate(dimensions[:4]):  # Limit to 4 rows
        # Background
        if i % 2 == 1:
            draw.rectangle([x, row_y, x + width, row_y + row_height], fill=BG_ROW_ALT)
        
        # Category name
        draw_metric_bar(draw, bar_x, row_y + scale(5), bar_width, bar_height,
                       score, 100, "", score_to_color(score))
        # Score bar
        score = data.get('score', 0)
        bar_x = x + scale(120)
        bar_width = scale(60)
        bar_height = scale(10)
        draw_metric_bar(draw, bar_x, row_y + scale(5), bar_width, bar_height,
                       score, 100, "", score_to_color(score))
        
        # Trend indicator
        trend_x = x + scale(200)
        trend_data = data.get('trend', [50, 55, 60])  # Sample trend data
        if len(trend_data) >= 2:
            trend = 'up' if trend_data[-1] > trend_data[-2] else 'down' if trend_data[-1] < trend_data[-2] else 'stable'
            draw_trend_indicator(draw, trend_x, row_y + scale(5), scale(20), trend, trend_data[-1])
        
        row_y += row_height


def draw_trends_section(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int,
    trends: Dict[str, List[float]]
) -> None:
    """Draw trends section with sparklines."""
    font_header = load_font(11, bold=True)
    
    # Section title
    draw.text((x, y), "Recent Trends", fill=TEXT, font=font_header)
    
    # Trend charts
    chart_y = y + scale(30)
    chart_width = scale(80)
    chart_height = scale(40)
    
    for i, (category, values) in enumerate(list(trends.items())[:2]):  # 2 charts
        chart_x = x + i * (chart_width + scale(20))
        
        # Category label
        font_label = load_font(9)
        draw.text((chart_x, chart_y - scale(15), category, fill=TEXT, font=font_label)
        # Sparkline
        draw_mini_sparkline(draw, chart_x, chart_y, chart_width, chart_height,
                           values, COLORS['info'])
        draw_mini_sparkline(draw, chart_x, chart_y, chart_width, chart_height, 
                           values, COLORS['info'])


def draw_recommendations_section(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int,
    recommendations: List[str]
) -> None:
    """Draw recommendations section."""
    font_header = load_font(12, bold=True)
    font_rec = load_font(9)
    
    # Section title
    draw.text((x, y), "🎯 Priority Actions", fill=TEXT, font=font_header)
    
    # Recommendations list
    rec_y = y + scale(25)
    for i, rec in enumerate(recommendations[:4]):  # Limit to 4 recommendations
        # Numbered bullet
        bullet = f"{i + 1}."
        draw.text((x + scale(10), rec_y, bullet, fill=ACCENT, font=font_rec)
        
        # Recommendation text (with word wrap)
        text_x = x + scale(30)
        max_width = width - scale(40)
        
        # Simple word wrap
        words = rec.split()
        line = ""
        for word in words:
            test_line = line + " " + word if line else word
            bbox = draw.textbbox((0, 0), test_line, font=font_rec)
            text_width = bbox[2] - bbox[0]
            
            if text_width > max_width or line:
                draw.text((text_x, rec_y), line, fill=TEXT, font=font_rec)
                rec_y += scale(12)
                line = word if line else ""
                text_x = x + scale(30)
            else:
                line = test_line
        
        rec_y += scale(15)


def draw_footer_section(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int,
    metadata: Dict[str, Any]
) -> None:
    """Draw footer with metadata."""
    font_footer = load_font(8)
    
    # Footer line
    draw.line([(x, y), (x + width, y)], fill=BORDER, width=1)
    
    # Metadata text
    files_analyzed = metadata.get('files_analyzed', 0)
    timestamp_str = metadata.get('timestamp', '')
    info_text = f"Generated by Devour v{metadata.get('version', '1.0.0')} - {files_analyzed} files analyzed - {timestamp_str}"
    draw.text((x, y + scale(5), info_text, fill=DIM, font=font_footer)


def draw_metric_bar(
    draw: ImageDraw.ImageDraw,
    x: int, y: int, width: int, height: int,
    value: float, max_value: float,
    label: str, color: Tuple[int, int, int]
) -> None:
    """Draw a horizontal metric bar."""
    # Background
    draw.rectangle([x, y, x + width, y + height], fill=BG_TABLE, outline=BORDER)
    
    # Fill bar
    if max_value > 0:
        fill_width = int((value / max_value) * width)
        draw.rectangle([x, y, x + fill_width, y + height], fill=color)


def load_enhanced_devour_data(json_path: str) -> EnhancedScorecardData:
    """Load Devour data and convert to enhanced format."""
    with open(json_path, 'r') as f:
        data = json.load(f)
    
    findings = data.get('findings', [])
    
    # Calculate scores
    total_score = sum(f.get('score', 0) * int(f.get('severity', 1)) for f in findings)
    main_score = max(0, 100 - (total_score / 1000 * 100))
    strict_score = main_score * 0.8  # Strict is lower
    
    # Group by type
    type_counts = {}
    type_scores = {}
    
    for finding in findings:
        ftype = finding.get('type', 'unknown')
        type_counts[ftype] = type_counts.get(ftype, 0) + 1
        type_scores[ftype] = type_scores.get(ftype, 0) + finding.get('score', 0)
    
    # Create dimensions with trend data
    dimensions = []
    for ftype, count in type_counts.items():
        avg_score = 100 - (type_scores[ftype] / max(1, count) / 10 * 100)
        
        # Generate sample trend data
        trend_data = [avg_score - 10, avg_score - 5, avg_score, avg_score + 5, avg_score + 10]
        
        dimensions.append((
            ftype.replace('_', ' ').title(),
            {
                'score': max(0, min(100, avg_score)),
                'strict': max(0, min(100, avg_score * 0.8)),
                'count': count,
                'trend': trend_data
            }
        ))
    
    # Sort by score (lowest first)
    dimensions.sort(key=lambda x: x[1]['score'])
    
    # Generate recommendations
    recommendations = [
        "🔴 Address critical T4 issues immediately for maximum impact",
        "🟡 Focus on reducing high-severity findings (T2-T3)",
        "🟢 Improve test coverage to reduce technical debt",
        "📊 Regular code reviews to maintain quality standards"
    ]
    
    # Metadata
    metadata = {
        'version': '1.0.0',
        'files_analyzed': len(set(f.get('file', '') for f in findings)),
        'timestamp': data.get('timestamp', '')
    }
    
    return EnhancedScorecardData(
        project_name="Devour",
        version="1.0.0",
        main_score=main_score,
        strict_score=strict_score,
        dimensions=dimensions[:6],  # Limit to 6 dimensions
        trends={'overall': [main_score - 5, main_score], 'performance': [85, 88, 92, main_score]},
        recommendations=recommendations,
        metadata=metadata
    )


def main():
    """Main entry point."""
    if len(sys.argv) != 3:
        print("Usage: python devour_enhanced.py <devour_results.json> <output.png>")
        sys.exit(1)
    
    json_path = sys.argv[1]
    output_path = sys.argv[2]
    
    if not os.path.exists(json_path):
        print(f"Error: Input file {json_path} not found")
        sys.exit(1)
    
    try:
        data = load_enhanced_devour_data(json_path)
        result_path = generate_enhanced_scorecard(data, output_path)
        print(f"Enhanced scorecard generated: {result_path}")
        
    except Exception as e:
        print(f"Error generating enhanced scorecard: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
