"""
Reporting functionality for Gattopy.
"""

import json
from pathlib import Path
from typing import Any, Dict, List, Optional

from .colors import Colors, colorize
from .core import ProfileResult


def format_profile_report(stats: Dict[str, Any]) -> str:
    """
    Format profiling statistics into a human-readable colored report.

    This is called by the @gatto decorator to display results.

    Args:
        stats: Dictionary containing profiling statistics

    Returns:
        Formatted string report with colors
    """
    lines = []

    # Header with cyan color
    separator = colorize("=" * 60, Colors.BRIGHT_BLACK)
    lines.append(separator)

    function_name = colorize(stats["function_name"], Colors.BRIGHT_CYAN, bold=True)
    lines.append(f"{colorize('Function:', Colors.WHITE)} {function_name}")
    lines.append(separator)

    # Show arguments if present
    if "arguments" in stats and stats["arguments"]:
        lines.append("")
        lines.append(colorize("Arguments:", Colors.YELLOW, bold=True))
        for arg_name, arg_value in stats["arguments"].items():
            arg_name_colored = colorize(arg_name, Colors.BRIGHT_YELLOW)
            arg_value_colored = colorize(str(arg_value), Colors.WHITE)
            lines.append(f"  {arg_name_colored} = {arg_value_colored}")

    # Show timing with green color
    if stats.get("execution_time") is not None:
        lines.append("")
        lines.append(colorize("Execution Time:", Colors.GREEN, bold=True))

        # Color the formatted time based on duration
        exec_time = stats["execution_time"]
        formatted_time = stats["execution_time_formatted"]

        if exec_time < 0.001:  # Very fast (< 1ms)
            time_color = Colors.BRIGHT_GREEN
        elif exec_time < 0.1:  # Fast (< 100ms)
            time_color = Colors.GREEN
        elif exec_time < 1.0:  # Moderate (< 1s)
            time_color = Colors.YELLOW
        else:  # Slow (>= 1s)
            time_color = Colors.RED

        lines.append(f"  {colorize(formatted_time, time_color, bold=True)}")
        lines.append(
            f"  {colorize('(raw:', Colors.DIM)} {colorize(f'{exec_time:.6f} seconds', Colors.BRIGHT_BLACK)}{colorize(')', Colors.DIM)}"
        )

    # Show memory if present with blue color
    if stats.get("memory_peak_mb") is not None:
        lines.append("")
        lines.append(colorize("Memory Usage:", Colors.BLUE, bold=True))

        # Format peak memory with appropriate units
        peak_mb = stats["memory_peak_mb"]
        if peak_mb < 0.001:  # Less than 1 KB
            peak_bytes = peak_mb * 1024 * 1024
            peak_text = f"{peak_bytes:.2f} bytes"
            memory_color = Colors.BRIGHT_GREEN
        elif peak_mb < 1:  # Less than 1 MB
            peak_kb = peak_mb * 1024
            peak_text = f"{peak_kb:.2f} KB"
            memory_color = Colors.GREEN
        elif peak_mb < 100:  # Less than 100 MB
            peak_text = f"{peak_mb:.2f} MB"
            memory_color = Colors.CYAN
        elif peak_mb < 1024:  # Less than 1 GB
            peak_text = f"{peak_mb:.2f} MB"
            memory_color = Colors.YELLOW
        else:  # 1 GB or more
            peak_gb = peak_mb / 1024
            peak_text = f"{peak_gb:.2f} GB"
            memory_color = Colors.RED

        lines.append(
            f"  {colorize('Peak:', Colors.BRIGHT_BLUE)} {colorize(peak_text, memory_color, bold=True)}"
        )

        # Format current memory with appropriate units
        if stats.get("memory_current_mb") is not None:
            current_mb = stats["memory_current_mb"]
            if current_mb < 0.001:  # Less than 1 KB
                current_bytes = current_mb * 1024 * 1024
                current_text = f"{current_bytes:.2f} bytes"
            elif current_mb < 1:  # Less than 1 MB
                current_kb = current_mb * 1024
                current_text = f"{current_kb:.2f} KB"
            elif current_mb < 1024:  # Less than 1 GB
                current_text = f"{current_mb:.2f} MB"
            else:  # 1 GB or more
                current_gb = current_mb / 1024
                current_text = f"{current_gb:.2f} GB"

            lines.append(
                f"  {colorize('Current:', Colors.BRIGHT_BLUE)} {colorize(current_text, Colors.CYAN)}"
            )

    lines.append(separator)

    return "\n".join(lines)


class Reporter:
    """Handle reporting and exporting of profiling results."""

    def __init__(self):
        self.results: List[ProfileResult] = []

    def add_result(self, result: ProfileResult) -> None:
        """Add a profiling result to the reporter."""
        self.results.append(result)

    def clear(self) -> None:
        """Clear all stored results."""
        self.results.clear()

    def generate_summary(self) -> str:
        """Generate a colored text summary of all results."""
        if not self.results:
            return colorize("No profiling results available.", Colors.YELLOW)

        separator = colorize("=" * 60, Colors.BRIGHT_BLACK)
        lines = [separator]
        title = colorize("GATTOPY PROFILING SUMMARY", Colors.BRIGHT_MAGENTA, bold=True)
        lines.append(title)
        lines.append(separator)
        lines.append("")

        for i, result in enumerate(self.results, 1):
            index = colorize(f"[{i}]", Colors.BRIGHT_CYAN, bold=True)
            lines.append(f"{index} {result}")
            lines.append("")

        # Add aggregate statistics
        if len(self.results) > 1:
            sub_separator = colorize("-" * 60, Colors.BRIGHT_BLACK)
            lines.append(sub_separator)
            aggregate_title = colorize(
                "AGGREGATE STATISTICS", Colors.BRIGHT_YELLOW, bold=True
            )
            lines.append(aggregate_title)
            lines.append(sub_separator)

            # Time statistics
            times = [
                r.execution_time for r in self.results if r.execution_time is not None
            ]
            if times:
                total = sum(times)
                avg = sum(times) / len(times)
                min_time = min(times)
                max_time = max(times)

                lines.append(
                    f"{colorize('Total Time:', Colors.GREEN)} {colorize(f'{total:.4f} s', Colors.BRIGHT_GREEN, bold=True)}"
                )
                lines.append(
                    f"{colorize('Average Time:', Colors.GREEN)} {colorize(f'{avg:.4f} s', Colors.BRIGHT_GREEN)}"
                )
                lines.append(
                    f"{colorize('Min Time:', Colors.GREEN)} {colorize(f'{min_time:.4f} s', Colors.BRIGHT_GREEN)}"
                )
                lines.append(
                    f"{colorize('Max Time:', Colors.GREEN)} {colorize(f'{max_time:.4f} s', Colors.BRIGHT_GREEN)}"
                )

            # Memory statistics
            peaks = [r.memory_peak for r in self.results if r.memory_peak is not None]
            if peaks:
                avg_peak = sum(peaks) / len(peaks)
                max_peak = max(peaks)

                lines.append(
                    f"{colorize('Average Peak Memory:', Colors.BLUE)} {colorize(f'{avg_peak:.2f} MB', Colors.BRIGHT_CYAN, bold=True)}"
                )
                lines.append(
                    f"{colorize('Max Peak Memory:', Colors.BLUE)} {colorize(f'{max_peak:.2f} MB', Colors.BRIGHT_CYAN, bold=True)}"
                )

        lines.append(separator)
        return "\n".join(lines)

    def print_summary(self) -> None:
        """Print the summary to console."""
        print(self.generate_summary())

    def to_dict(self) -> Dict[str, Any]:
        """Convert all results to a dictionary."""
        return {
            "total_results": len(self.results),
            "results": [result.to_dict() for result in self.results],
            "summary": self._generate_summary_dict(),
        }

    def _generate_summary_dict(self) -> Dict[str, Any]:
        """Generate summary statistics as dictionary."""
        if not self.results:
            return {}

        summary = {}

        # Time statistics
        times = [r.execution_time for r in self.results if r.execution_time is not None]
        if times:
            summary["time"] = {
                "total": sum(times),
                "average": sum(times) / len(times),
                "min": min(times),
                "max": max(times),
                "count": len(times),
            }

        # Memory statistics
        peaks = [r.memory_peak for r in self.results if r.memory_peak is not None]
        if peaks:
            summary["memory_peak_mb"] = {
                "average": sum(peaks) / len(peaks),
                "min": min(peaks),
                "max": max(peaks),
                "count": len(peaks),
            }

        return summary

    def save_json(self, filepath: str) -> None:
        """
        Save results to a JSON file.

        Args:
            filepath: Path to the output JSON file
        """
        data = self.to_dict()

        path = Path(filepath)
        path.parent.mkdir(parents=True, exist_ok=True)

        with open(path, "w") as f:
            json.dump(data, f, indent=2)

    def save_csv(self, filepath: str) -> None:
        """
        Save results to a CSV file.

        Args:
            filepath: Path to the output CSV file
        """
        if not self.results:
            return

        path = Path(filepath)
        path.parent.mkdir(parents=True, exist_ok=True)

        with open(path, "w") as f:
            # Write header
            f.write(
                "function_name,execution_time,memory_peak_mb,memory_current_mb,calls_count\n"
            )

            # Write data
            for result in self.results:
                f.write(f"{result.function_name},")
                f.write(f"{result.execution_time or ''},")
                f.write(f"{result.memory_peak or ''},")
                f.write(f"{result.memory_current or ''},")
                f.write(f"{result.calls_count}\n")

    def save_markdown(self, filepath: str) -> None:
        """
        Save results to a Markdown file.

        Args:
            filepath: Path to the output Markdown file
        """
        if not self.results:
            return

        path = Path(filepath)
        path.parent.mkdir(parents=True, exist_ok=True)

        with open(path, "w") as f:
            f.write("# Gattopy Profiling Report\n\n")

            # Create table
            f.write(
                "| Function | Execution Time | Peak Memory | Current Memory | Calls |\n"
            )
            f.write(
                "|----------|----------------|-------------|----------------|-------|\n"
            )

            for result in self.results:
                time_str = (
                    f"{result.execution_time:.4f}s" if result.execution_time else "N/A"
                )
                peak_str = (
                    f"{result.memory_peak:.2f}MB" if result.memory_peak else "N/A"
                )
                current_str = (
                    f"{result.memory_current:.2f}MB" if result.memory_current else "N/A"
                )

                f.write(
                    f"| `{result.function_name}` | {time_str} | {peak_str} | {current_str} | {result.calls_count} |\n"
                )

            # Add summary section
            if len(self.results) > 1:
                f.write("\n## Summary Statistics\n\n")
                summary = self._generate_summary_dict()

                if "time" in summary:
                    f.write("### Time\n\n")
                    f.write(f"- **Total**: {summary['time']['total']:.4f}s\n")
                    f.write(f"- **Average**: {summary['time']['average']:.4f}s\n")
                    f.write(f"- **Min**: {summary['time']['min']:.4f}s\n")
                    f.write(f"- **Max**: {summary['time']['max']:.4f}s\n\n")

                if "memory_peak_mb" in summary:
                    f.write("### Memory (Peak)\n\n")
                    f.write(
                        f"- **Average**: {summary['memory_peak_mb']['average']:.2f}MB\n"
                    )
                    f.write(f"- **Min**: {summary['memory_peak_mb']['min']:.2f}MB\n")
                    f.write(f"- **Max**: {summary['memory_peak_mb']['max']:.2f}MB\n")
