import logging
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional

import pandas as pd
import yaml  # Added for EXPLAIN plan serialization

from .analyzer import SlowQuery
from .llm_client import LLMClient

logger = logging.getLogger(__name__)


class ReportGenerator:
    """Generates comprehensive analysis reports with AI recommendations
    and anti-pattern detection."""

    def __init__(self, llm_client: LLMClient, output_dir: str = "reports"):
        self.llm_client = llm_client
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(exist_ok=True)
        logger.info(f"Report generator initialized with output dir: {output_dir}")

    def generate_markdown_report(
        self,
        top_queries: Optional[pd.DataFrame],
        summary: Optional[Dict[str, Any]],
        recommendations: Optional[list] = None,
        contract_results: Optional[List[Any]] = None,
    ) -> str:
        """
        Generate a Markdown report

        Args:
            top_queries: DataFrame with top slow queries
            summary: Dictionary with summary statistics
            recommendations: Optional list of LLM recommendations

        Returns:
            Report text as string
        """
        lines = []

        # Header
        lines.append("# PostgreSQL Performance Analysis Report")
        lines.append(
            f"\n**Generated:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
        )

        if summary:
            lines.append("## Summary Statistics\n")
            lines.append(f"- **Total Queries Analyzed:** {summary['total_queries']}")
            lines.append(f"- **Unique Query Patterns:** {summary['unique_queries']}")
            lines.append(
                f"- **Average Duration:** {summary['avg_duration_overall']:.2f} ms"
            )
            lines.append(
                f"- **Max Duration:** {summary['max_duration_overall']:.2f} ms"
            )
            lines.append(f"- **P95 Duration:** {summary['p95_duration']:.2f} ms")
            lines.append(f"- **P99 Duration:** {summary['p99_duration']:.2f} ms")
            lines.append(
                f"- **Total Time Spent:** "
                f"{summary['total_time_spent'] / 1000:.2f} seconds\n"
            )

        if contract_results:
            lines.append(self._format_contract_summary(contract_results))

        if top_queries is not None and not top_queries.empty:
            lines.append("## Top Slow Queries (by Impact)\n")

            for rank, (_, row) in enumerate(top_queries.iterrows(), start=1):
                lines.append(f"### Query #{rank}\n")
                lines.append("```sql")
                lines.append(row["example_query"][:500])
                lines.append("```\n")
                lines.append(f"- **Average Duration:** {row['avg_duration']:.2f} ms")
                lines.append(f"- **Max Duration:** {row['max_duration']:.2f} ms")
                lines.append(f"- **Frequency:** {row['frequency']} executions")
                lines.append(f"- **Impact Score:** {row['impact_score']:.2f}\n")

                if recommendations and rank - 1 < len(recommendations):
                    lines.append("**AI Recommendation:**\n")
                    lines.append(f"{recommendations[rank - 1]}\n")

                lines.append("---\n")

        if contract_results:
            lines.append(self._format_contract_details(contract_results))

        return "\n".join(lines)

    def generate_report(
        self,
        top_queries: List[SlowQuery],
        all_queries: List[SlowQuery],
        contract_results: Optional[List[Any]] = None,
    ) -> str:
        """
        Generate a comprehensive slow query analysis report.

        Args:
            top_queries: Top slow queries to analyze in detail
            all_queries: All analyzed queries for summary statistics

        Returns:
            Formatted Markdown report
        """
        report = []

        # Header
        report.append("# 🩺 Slow Query Analysis Report")
        report.append(f"*Generated on {self._get_current_timestamp()}*\n")

        # Summary section
        report.append(self._generate_summary(all_queries))

        if contract_results:
            report.append(self._format_contract_summary(contract_results))

        # Top queries analysis
        if top_queries:
            report.append("## 🐌 Top Slow Queries Analysis\n")

            for i, query in enumerate(top_queries, 1):
                report.append(self._generate_query_analysis(query, i))

        # Anti-pattern summary
        report.append(self._generate_antipattern_summary(all_queries))

        # Recommendations summary
        report.append(self._generate_recommendations_summary(all_queries))

        return "\n".join(report)

    def _generate_summary(self, queries: List[SlowQuery]) -> str:
        """Generate executive summary of all analyzed queries."""
        summary: List[str] = []
        summary.append("## 📊 Executive Summary\n")

        if not queries:
            summary.append("No queries to analyze.")
            summary.append("")
            return "\n".join(summary)

        # Basic statistics
        total_queries = len(queries)
        avg_duration = sum(q.duration for q in queries) / total_queries
        total_duration = sum(q.duration for q in queries)
        avg_impact = sum(q.impact_score for q in queries) / total_queries
        avg_optimization = sum(q.optimization_score for q in queries) / total_queries

        summary.append(f"- **Total Queries Analyzed**: {total_queries}")
        summary.append(f"- **Average Duration**: {avg_duration:.2f} ms")
        summary.append(f"- **Total Duration**: {total_duration:.2f} ms")
        summary.append(f"- **Average Impact Score**: {avg_impact:.2f}")
        summary.append(f"- **Average Optimization Score**: {avg_optimization:.1%}")

        # Performance categories
        slow_queries = [q for q in queries if q.duration > 1000]  # > 1 second
        very_slow_queries = [q for q in queries if q.duration > 5000]  # > 5 seconds
        high_impact_queries = [q for q in queries if q.impact_score > 80]

        if slow_queries:
            summary.append(
                f"- **Slow Queries (>1s)**: {len(slow_queries)} "
                f"({len(slow_queries) / total_queries * 100:.1f}%)"
            )
        if very_slow_queries:
            summary.append(
                f"- **Very Slow Queries (>5s)**: {len(very_slow_queries)} "
                f"({len(very_slow_queries) / total_queries * 100:.1f}%)"
            )
        if high_impact_queries:
            summary.append(
                f"- **High Impact Queries**: {len(high_impact_queries)} "
                f"({len(high_impact_queries) / total_queries * 100:.1f}%)"
            )

        summary.append("")
        return "\n".join(summary)

    def _format_contract_summary(self, contract_results: List[Any]) -> str:
        """Format contract-based summary details."""
        total = len(contract_results)
        severity_counts: Dict[str, int] = {}
        findings = 0
        recommendations = 0

        for result in contract_results:
            severity = self._extract_contract_severity(result)
            if severity:
                severity_counts[severity] = severity_counts.get(severity, 0) + 1

            findings += self._extract_contract_count(result, "findings")
            recommendations += self._extract_contract_count(result, "recommendations")

        lines = ["## Contract Summary", ""]
        lines.append(f"- **Contract Results:** {total}")
        lines.append(f"- **Total Findings:** {findings}")
        lines.append(f"- **Total Recommendations:** {recommendations}")

        if severity_counts:
            lines.append("- **Severity Breakdown:**")
            for severity, count in sorted(severity_counts.items()):
                lines.append(f"  - {severity}: {count}")

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

    def _format_contract_details(self, contract_results: List[Any]) -> str:
        lines = ["## Contract Findings", ""]

        for index, result in enumerate(contract_results, start=1):
            query_text = self._extract_contract_query_text(result)
            severity = self._extract_contract_severity(result) or "Unknown"
            findings = self._extract_contract_items(result, "findings")
            recommendations = self._extract_contract_items(result, "recommendations")

            lines.append(f"### Result #{index}")
            if query_text:
                lines.append("```sql")
                lines.append(query_text[:500])
                lines.append("```")
            lines.append(f"- **Severity:** {severity}")
            lines.append(f"- **Findings:** {len(findings)}")
            lines.append(f"- **Recommendations:** {len(recommendations)}")

            if findings:
                lines.append("**Findings:**")
                for finding in findings[:5]:
                    name = self._extract_contract_item_field(finding, "name")
                    description = self._extract_contract_item_field(
                        finding, "description"
                    )
                    if name and description:
                        lines.append(f"- {name}: {description}")
                    elif description:
                        lines.append(f"- {description}")
                    elif name:
                        lines.append(f"- {name}")

            if recommendations:
                lines.append("**Recommendations:**")
                for rec in recommendations[:5]:
                    text = self._extract_contract_item_field(rec, "recommendation")
                    if text:
                        lines.append(f"- {text}")

            lines.append("")

        return "\n".join(lines)

    @staticmethod
    def _extract_contract_items(result: Any, field: str) -> List[Any]:
        items = None
        if isinstance(result, dict):
            items = result.get(field)
        else:
            items = getattr(result, field, None)

        if not items:
            return []

        return list(items)

    @staticmethod
    def _extract_contract_item_field(item: Any, field: str) -> Optional[str]:
        if isinstance(item, dict):
            value = item.get(field)
        else:
            value = getattr(item, field, None)

        if value is None:
            return None
        return str(value)

    @staticmethod
    def _extract_contract_query_text(result: Any) -> Optional[str]:
        query = None
        if isinstance(result, dict):
            query = result.get("query")
        else:
            query = getattr(result, "query", None)

        if query is None:
            return None

        if isinstance(query, dict):
            return query.get("query_text")

        return getattr(query, "query_text", None)

    @staticmethod
    def _extract_contract_severity(result: Any) -> Optional[str]:
        value = None
        if isinstance(result, dict):
            value = result.get("severity")
        else:
            value = getattr(result, "severity", None)

        if value is None:
            return None

        if hasattr(value, "value"):
            return str(value.value).title()
        return str(value).title()

    @staticmethod
    def _extract_contract_count(result: Any, field: str) -> int:
        items = None
        if isinstance(result, dict):
            items = result.get(field)
        else:
            items = getattr(result, field, None)

        if not items:
            return 0
        return len(items)

    def _generate_query_analysis(self, query: SlowQuery, rank: int) -> str:
        """Generate detailed analysis for a single query."""
        analysis = []

        # Query header
        analysis.append(f"### Query #{rank}: {self._get_query_title(query)}")
        analysis.append(
            f"**Impact Score**: {query.impact_score:.2f} | "
            f"**Duration**: {query.duration:.2f}ms | "
            f"**Frequency**: {query.frequency} | "
            f"**Optimization Score**: {query.optimization_score:.1%}"
        )
        analysis.append(f"**First seen**: {query.timestamp}\n")

        # Query code block
        analysis.append("```sql")
        analysis.append(query.normalized_query)
        analysis.append("```\n")

        # Anti-pattern analysis (if any issues found)
        if query.antipattern_matches:
            analysis.append("#### 🔍 Static Analysis Issues")
            analysis.append(query.static_analysis_report)

        # EXPLAIN plan analysis
        if query.explain_plan:
            analysis.append("#### 📋 EXPLAIN Plan Analysis")

            if query.index_recommendations:
                analysis.append("**Index Recommendations:**")
                for rec in query.index_recommendations:
                    columns_str = ", ".join(rec.columns) if rec.columns else "N/A"
                    analysis.append(
                        f"- **Table**: {rec.table}, **Columns**: {columns_str}"
                    )
                    analysis.append(f"  - **Reason**: {rec.reason}")
                analysis.append("")

            if query.explain_antipatterns:
                analysis.append("**Detected Anti-Patterns:**")
                for ap in query.explain_antipatterns:
                    analysis.append(f"- **{ap.pattern_type.value}**: {ap.description}")
                analysis.append("")

            analysis.append("```yaml")
            if query.explain_metrics:
                metrics = query.explain_metrics
                analysis.append(f"  Total Nodes: {metrics.total_nodes}")
                if metrics.execution_time:
                    analysis.append(
                        f"  Execution Time (from plan): {metrics.execution_time:.2f} ms"
                    )
                if metrics.max_rows_discrepancy is not None:
                    analysis.append(
                        f"  Max Rows Discrepancy: {metrics.max_rows_discrepancy:.1%}"
                        f" (Node: {metrics.max_rows_discrepancy_node})"
                    )
                if metrics.large_seq_scans > 0:
                    analysis.append(
                        f"  Large Sequential Scans: {metrics.large_seq_scans}"
                    )

            analysis.append(
                yaml.dump(
                    query.explain_plan.model_dump(exclude_none=True),
                    sort_keys=False,
                    indent=2,
                )
            )
            analysis.append("```\n")

        # AI-powered recommendations
        try:
            explain_plan_json = None
            if query.explain_plan:
                explain_plan_json = yaml.dump(
                    query.explain_plan.model_dump(exclude_none=True),
                    sort_keys=False,
                    indent=2,
                )

            ai_recommendation = self.llm_client.generate_recommendations(
                query.normalized_query,
                query.duration,
                query.frequency,
                explain_plan=explain_plan_json,
            )
            analysis.append("#### 🤖 AI-Powered Optimization Recommendations")
            analysis.append(ai_recommendation)
        except Exception as e:
            logger.warning(f"Failed to get AI recommendation for query {rank}: {e}")
            analysis.append("#### 🤖 AI Analysis")
            analysis.append("*AI analysis temporarily unavailable*")

        analysis.append("\n---\n")
        return "\n".join(analysis)

    def _generate_antipattern_summary(self, queries: List[SlowQuery]) -> str:
        """Generate summary of anti-patterns found across all queries."""
        summary = []
        summary.append("## 🚨 Anti-Pattern Analysis Summary\n")

        # Count anti-patterns across all queries
        pattern_counts: dict[str, int] = {}
        total_issues = 0
        queries_with_issues = 0

        for query in queries:
            if query.antipattern_matches:
                queries_with_issues += 1
                for match in query.antipattern_matches:
                    pattern_type = match.pattern_type.value.replace("_", " ").title()
                    pattern_counts[pattern_type] = (
                        pattern_counts.get(pattern_type, 0) + 1
                    )
                    total_issues += 1

        if total_issues == 0:
            summary.append(
                "✅ **No anti-patterns detected** - Your queries follow good practices!"
            )
            summary.append("")
            return "\n".join(summary)

        # Statistics
        summary.append(f"- **Total Issues Found**: {total_issues}")
        summary.append(
            f"- **Queries with Issues**: {queries_with_issues}/"
            f"{len(queries)} "
            f"({queries_with_issues / len(queries) * 100:.1f}%)"
        )
        summary.append(
            f"- **Average Optimization Score**: "
            f"{sum(q.optimization_score for q in queries) / len(queries):.1%}"
        )
        summary.append("")

        # Most common anti-patterns
        if pattern_counts:
            summary.append("### Most Common Anti-Patterns")
            for pattern, count in sorted(
                pattern_counts.items(), key=lambda x: x[1], reverse=True
            ):
                summary.append(
                    f"- **{pattern}**: {count} occurrence{'s' if count > 1 else ''}"
                )
            summary.append("")

        return "\n".join(summary)

    def _generate_recommendations_summary(self, queries: List[SlowQuery]) -> str:
        """Generate summary of key recommendations."""
        summary = []
        summary.append("## 💡 Key Recommendations\n")

        # Priority recommendations based on anti-patterns
        high_impact = [q for q in queries if q.optimization_score < 0.7]

        if high_impact:
            summary.append("### 🔥 High Priority")
            for query in high_impact[:3]:  # Top 3 high-impact queries
                summary.append(
                    f"- **Query with impact score {query.impact_score:.0f}**: "
                    f"Optimization score {query.optimization_score:.1%} - "
                    f"{len(query.antipattern_matches)} anti-pattern"
                    f"{'s' if len(query.antipattern_matches) > 1 else ''} "
                    f"detected"
                )
            summary.append("")

        # General recommendations
        summary.append("### 📋 General Recommendations")
        summary.append(
            "1. **Index Analysis**: Review missing indexes for columns in WHERE clauses"
        )
        summary.append(
            "2. **Query Patterns**: Address anti-patterns identified in static analysis"
        )
        summary.append(
            "3. **Monitoring**: Set up regular monitoring for queries "
            "with high impact scores"
        )
        summary.append(
            "4. **Testing**: Validate optimizations in a staging environment first"
        )
        summary.append("")

        return "\n".join(summary)

    def _get_current_timestamp(self) -> str:
        """Get the current timestamp as a formatted string."""
        return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    def _get_query_title(self, query: SlowQuery) -> str:
        """Generate a title for the query section in the report."""
        return (
            f"Impact Score: {query.impact_score:.2f} | "
            f"Duration: {query.duration:.2f} ms | "
            f"Frequency: {query.frequency}"
        )
