"""Output formatting for policy evaluation results."""

from __future__ import annotations

import json
from dataclasses import asdict, dataclass, field
from datetime import UTC, datetime
from enum import Enum
from typing import Any

from .rules import RuleResult


class ReportFormat(Enum):
    """Output format for reports."""

    JSON = "json"
    SARIF = "sarif"
    MARKDOWN = "markdown"
    TEXT = "text"


@dataclass
class Violation:
    """A single policy violation."""

    rule_id: str
    rule_name: str
    message: str
    severity: str
    file_path: str
    line_no: int
    column_start: int
    column_end: int
    matched_text: str
    line_content: str
    remediation: str = ""

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)


@dataclass
class EvaluationSummary:
    """Summary of an evaluation run."""

    total_files: int
    files_with_violations: int
    total_violations: int
    errors: int
    warnings: int
    infos: int
    passed: bool


@dataclass
class EvaluationReport:
    """Complete evaluation report."""

    evaluation_id: str
    timestamp: str
    duration_ms: int
    summary: EvaluationSummary
    violations: list[Violation] = field(default_factory=list)
    files_checked: list[str] = field(default_factory=list)

    @classmethod
    def create(
        cls,
        evaluation_id: str,
        violations: list[Violation],
        files_checked: list[str],
        duration_ms: int,
    ) -> EvaluationReport:
        """Create an evaluation report from violations."""
        files_with_violations = len({v.file_path for v in violations})
        errors = sum(1 for v in violations if v.severity == "error")
        warnings = sum(1 for v in violations if v.severity == "warning")
        infos = sum(1 for v in violations if v.severity == "info")

        summary = EvaluationSummary(
            total_files=len(files_checked),
            files_with_violations=files_with_violations,
            total_violations=len(violations),
            errors=errors,
            warnings=warnings,
            infos=infos,
            passed=errors == 0,
        )

        return cls(
            evaluation_id=evaluation_id,
            timestamp=datetime.now(UTC).isoformat(),
            duration_ms=duration_ms,
            summary=summary,
            violations=violations,
            files_checked=files_checked,
        )


class Reporter:
    """Generate formatted reports from evaluation results."""

    def __init__(self, tool_name: str = "mergeguide", tool_version: str = "0.1.0") -> None:
        self.tool_name = tool_name
        self.tool_version = tool_version

    def format(
        self,
        report: EvaluationReport,
        format_type: ReportFormat = ReportFormat.JSON,
    ) -> str:
        """Format an evaluation report."""
        if format_type == ReportFormat.JSON:
            return self._format_json(report)
        elif format_type == ReportFormat.SARIF:
            return self._format_sarif(report)
        elif format_type == ReportFormat.MARKDOWN:
            return self._format_markdown(report)
        elif format_type == ReportFormat.TEXT:
            return self._format_text(report)
        else:
            raise ValueError(f"Unknown format: {format_type}")

    def violations_from_results(
        self,
        results: list[RuleResult],
        file_path: str,
        rules_map: dict[str, dict[str, str]],
    ) -> list[Violation]:
        """Convert rule results to violations."""
        violations = []
        for result in results:
            if not result.passed:
                rule_info = rules_map.get(result.rule_id, {})
                for match in result.matches:
                    violations.append(
                        Violation(
                            rule_id=result.rule_id,
                            rule_name=rule_info.get("name", result.rule_id),
                            message=result.message,
                            severity=result.severity.value,
                            file_path=file_path,
                            line_no=match.line_no,
                            column_start=match.column_start,
                            column_end=match.column_end,
                            matched_text=match.matched_text,
                            line_content=match.line_content,
                            remediation=rule_info.get("remediation", ""),
                        )
                    )
        return violations

    def _format_json(self, report: EvaluationReport) -> str:
        """Format as JSON."""
        data = {
            "evaluation_id": report.evaluation_id,
            "timestamp": report.timestamp,
            "duration_ms": report.duration_ms,
            "summary": asdict(report.summary),
            "violations": [v.to_dict() for v in report.violations],
            "files_checked": report.files_checked,
        }
        return json.dumps(data, indent=2)

    def _format_sarif(self, report: EvaluationReport) -> str:
        """Format as SARIF 2.1.0."""
        # Group violations by rule
        rules_seen: dict[str, dict[str, Any]] = {}
        results = []

        for violation in report.violations:
            # Add rule if not seen
            if violation.rule_id not in rules_seen:
                rules_seen[violation.rule_id] = {
                    "id": violation.rule_id,
                    "name": violation.rule_name,
                    "shortDescription": {"text": violation.message},
                    "defaultConfiguration": {
                        "level": self._sarif_level(violation.severity)
                    },
                    "helpUri": f"https://mergeguide.ai/rules/{violation.rule_id}",
                }
                if violation.remediation:
                    rules_seen[violation.rule_id]["help"] = {
                        "text": violation.remediation
                    }

            # Add result
            results.append({
                "ruleId": violation.rule_id,
                "ruleIndex": list(rules_seen.keys()).index(violation.rule_id),
                "level": self._sarif_level(violation.severity),
                "message": {"text": violation.message},
                "locations": [{
                    "physicalLocation": {
                        "artifactLocation": {
                            "uri": violation.file_path,
                            "uriBaseId": "%SRCROOT%",
                        },
                        "region": {
                            "startLine": violation.line_no,
                            "startColumn": violation.column_start,
                            "endColumn": violation.column_end,
                            "snippet": {"text": violation.line_content},
                        },
                    }
                }],
            })

        sarif = {
            "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
            "version": "2.1.0",
            "runs": [{
                "tool": {
                    "driver": {
                        "name": self.tool_name,
                        "version": self.tool_version,
                        "informationUri": "https://github.com/MergeGuide/mergeguide",
                        "rules": list(rules_seen.values()),
                    }
                },
                "results": results,
                "invocations": [{
                    "executionSuccessful": report.summary.passed,
                    "endTimeUtc": report.timestamp,
                }],
            }],
        }

        return json.dumps(sarif, indent=2)

    def _format_markdown(self, report: EvaluationReport) -> str:
        """Format as Markdown."""
        lines = [
            "# MergeGuide Policy Evaluation Report",
            "",
            f"**Evaluation ID:** `{report.evaluation_id}`",
            f"**Timestamp:** {report.timestamp}",
            f"**Duration:** {report.duration_ms}ms",
            "",
            "## Summary",
            "",
            "| Metric | Value |",
            "|--------|-------|",
            f"| Files Checked | {report.summary.total_files} |",
            f"| Files with Violations | {report.summary.files_with_violations} |",
            f"| Total Violations | {report.summary.total_violations} |",
            f"| Errors | {report.summary.errors} |",
            f"| Warnings | {report.summary.warnings} |",
            f"| Info | {report.summary.infos} |",
            f"| **Status** | {'PASSED' if report.summary.passed else 'FAILED'} |",
            "",
        ]

        if report.violations:
            lines.extend([
                "## Violations",
                "",
            ])

            # Group by file
            by_file: dict[str, list[Violation]] = {}
            for v in report.violations:
                by_file.setdefault(v.file_path, []).append(v)

            for file_path, violations in by_file.items():
                lines.append(f"### `{file_path}`")
                lines.append("")

                for v in violations:
                    severity_emoji = {"error": "X", "warning": "!", "info": "i"}.get(
                        v.severity, "?"
                    )
                    lines.extend([
                        f"- **[{severity_emoji}] {v.rule_name}** (line {v.line_no})",
                        f"  - {v.message}",
                        f"  - `{v.matched_text}`",
                    ])
                    if v.remediation:
                        lines.append(f"  - Remediation: {v.remediation}")
                    lines.append("")

        return "\n".join(lines)

    def _format_text(self, report: EvaluationReport) -> str:
        """Format as plain text."""
        lines = [
            "MergeGuide Policy Evaluation Report",
            "=" * 40,
            f"Evaluation ID: {report.evaluation_id}",
            f"Timestamp: {report.timestamp}",
            f"Duration: {report.duration_ms}ms",
            "",
            f"Files Checked: {report.summary.total_files}",
            (
                f"Violations: {report.summary.total_violations} "
                f"({report.summary.errors} errors, {report.summary.warnings} warnings, "
                f"{report.summary.infos} info)"
            ),
            f"Status: {'PASSED' if report.summary.passed else 'FAILED'}",
            "",
        ]

        if report.violations:
            lines.append("Violations:")
            lines.append("-" * 40)

            for v in report.violations:
                lines.extend([
                    f"{v.file_path}:{v.line_no}:{v.column_start}",
                    f"  [{v.severity.upper()}] {v.rule_name}: {v.message}",
                    f"  > {v.line_content.strip()}",
                    "",
                ])

        return "\n".join(lines)

    def _sarif_level(self, severity: str) -> str:
        """Convert severity to SARIF level."""
        return {
            "error": "error",
            "warning": "warning",
            "info": "note",
        }.get(severity, "warning")
