from __future__ import annotations

import json
from typing import List

from .models import PluginReport, ProjectReport


def format_plugin_text(report: PluginReport) -> str:
    meta = report.metadata
    lines = []
    lines.append(f"Plugin: {meta.name or report.plugin_path} ({meta.version or 'n/a'})")
    lines.append(f"Path: {report.resolved_path}")
    lines.append(f"WP: {report.wp_version} | PHP: {report.php_version}")
    lines.append(f"Files: {report.stats.total_files} | Size: {report.stats.code_bytes} bytes | PHP LOC: {report.stats.loc_php}")
    lines.append(f"Score: {report.score} | Grade: {report.grade}")
    if report.warnings:
        lines.append("Warnings:")
        for w in report.warnings:
            lines.append(f"  - {w}")
    if report.attack_surface:
        lines.append("Attack surface:")
        for a in report.attack_surface:
            lines.append(f"  - {a.hook_type}: {a.hook} -> {a.callback} ({a.file}:{a.line})")
    if report.findings:
        lines.append("Findings:")
        for f in report.findings:
            loc = f"{f.file}:{f.line}" if f.file and f.line else f.file or ""
            lines.append(f"  - [{f.severity.value}] {f.category}: {f.message} {loc}".rstrip())
            if f.evidence:
                lines.append(f"      evidence: {f.evidence}")
            if f.remediation:
                lines.append(f"      fix: {f.remediation}")
    else:
        lines.append("Findings: none")
    return "\n".join(lines)


def format_plugin_markdown(report: PluginReport) -> str:
    meta = report.metadata
    md = []
    md.append(f"# {meta.name or report.plugin_path} ({meta.version or 'n/a'})")
    md.append(f"- Path: `{report.resolved_path}`")
    md.append(f"- WordPress: `{report.wp_version}` | PHP: `{report.php_version}`")
    md.append(f"- Files: {report.stats.total_files} | Size: {report.stats.code_bytes} bytes | PHP LOC: {report.stats.loc_php}")
    md.append(f"- Score: **{report.score}** | Grade: **{report.grade}**")
    if report.attack_surface:
        md.append("\n## Attack surface")
        for a in report.attack_surface:
            md.append(f"- **{a.hook_type}** `{a.hook}` → `{a.callback}` ({a.file}:{a.line})")
    if report.findings:
        md.append("\n## Findings")
        for f in report.findings:
            loc = f"{f.file}:{f.line}" if f.file and f.line else f.file or ""
            md.append(f"- **[{f.severity.value.upper()}] {f.category}** - {f.message} ({loc})")
            if f.evidence:
                md.append(f"  - Evidence: `{f.evidence}`")
            if f.remediation:
                md.append(f"  - Fix: {f.remediation}")
    else:
        md.append("\n## Findings\n- High confidence: no critical issues found.")
    return "\n".join(md)


def format_project_text(project: ProjectReport) -> str:
    lines = [f"Scan target: {project.target}", f"Plugins: {len(project.plugin_reports)}"]
    if project.summary:
        lines.append(f"Findings: {project.summary.get('findings', 0)} (by severity: { {k:v for k,v in project.summary.items() if k.startswith('severity_')} })")
        grade_map = {k.replace('grade_', ''): v for k, v in project.summary.items() if k.startswith('grade_')}
        lines.append(f"Grades: {grade_map}")
    for pr in project.plugin_reports:
        lines.append("")
        lines.append(format_plugin_text(pr))
    return "\n".join(lines)


def format_project_markdown(project: ProjectReport) -> str:
    md = [f"# Ferruccio scan: {project.target}", f"- Plugins: {len(project.plugin_reports)}"]
    if project.summary:
        by_sev = {k.replace('severity_', ''): v for k, v in project.summary.items() if k.startswith("severity_")}
        grade_map = {k.replace('grade_', ''): v for k, v in project.summary.items() if k.startswith("grade_")}
        md.append(f"- Findings: {project.summary.get('findings', 0)} (by severity: {by_sev})")
        md.append(f"- Grades: {grade_map}")
    for pr in project.plugin_reports:
        md.append("\n---\n")
        md.append(format_plugin_markdown(pr))
    return "\n".join(md)


def to_json(obj) -> str:
    def default(o):
        if hasattr(o, "__dict__"):
            return o.__dict__
        if isinstance(o, set):
            return list(o)
        raise TypeError(f"Unserializable object {o}")

    return json.dumps(obj, default=default, indent=2)
