"""Text formatting for analytics models."""

from __future__ import annotations

import json

import attrs

from blackbox.models import (
    CodeflashSession,
    ProjectStats,
    Recommendation,
    SessionAudit,
    SessionDigest,
    SessionMeta,
    arrow,
    sparkline,
)


class MetaFormatter:
    """Formats a SessionMeta for display."""

    def __init__(self, meta: SessionMeta) -> None:
        self.meta = meta

    def summary(self) -> str:
        """Format as a human-readable summary."""
        m = self.meta
        lines = [
            f"Session {m.session_id[:8]} ({m.duration_minutes:.0f}min):",
            f"  Messages: {m.user_messages} user / {m.assistant_messages} assistant",
            f"  Tools: {m.tool_calls} calls ({m.tool_errors} errors)",
            f"  Tokens: {m.input_tokens:,} in / {m.output_tokens:,} out (cache hit {m.cache_hit_rate:.0%})",
        ]
        if m.git_commits:
            lines.append(f"  Git: {m.git_commits} commits on {m.git_branch or 'unknown'}")
        if m.files_modified:
            lines.append(f"  Files: {m.files_modified} modified (+{m.lines_added}/-{m.lines_removed})")
        if m.compactions:
            lines.append(f"  Compactions: {m.compactions}")
        if m.user_interruptions:
            lines.append(f"  Interruptions: {m.user_interruptions}")
        if m.thinking_blocks:
            lines.append(f"  Thinking blocks: {m.thinking_blocks}")
        if m.web_searches or m.web_fetches:
            lines.append(f"  Web: {m.web_searches} searches / {m.web_fetches} fetches")
        if m.permission_mode:
            lines.append(f"  Permission mode: {m.permission_mode}")
        top = sorted(m.tool_counts.items(), key=lambda x: x[1], reverse=True)[:5]
        if top:
            lines.append(f"  Top tools: {', '.join(f'{n}={c}' for n, c in top)}")
        return "\n".join(lines)


class AuditFormatter:
    """Formats a SessionAudit for display."""

    def __init__(self, audit: SessionAudit) -> None:
        self.audit = audit

    def summary(self) -> str:
        """Format as a human-readable summary."""
        a = self.audit
        lines = [
            f"Audit for {a.session_id[:8]}:",
            f"  Outcome: {a.outcome} | Satisfaction: {a.satisfaction}",
            f"  Type: {a.session_type}",
        ]
        if a.goal_categories:
            goals = ", ".join(
                f"{k}({v})" for k, v in sorted(a.goal_categories.items(), key=lambda x: x[1], reverse=True)[:3]
            )
            lines.append(f"  Goals: {goals}")
        if a.friction_counts:
            frictions = ", ".join(
                f"{k}({v})" for k, v in sorted(a.friction_counts.items(), key=lambda x: x[1], reverse=True)[:3]
            )
            lines.append(f"  Friction: {frictions}")
        if a.user_instructions:
            lines.append(f"  Instructions: {len(a.user_instructions)} extracted")
        if a.summary:
            lines.append(f"  Summary: {a.summary[:120]}")
        return "\n".join(lines)


class RecommendationFormatter:
    """Formats a Recommendation for display."""

    def __init__(self, rec: Recommendation) -> None:
        self.rec = rec

    def summary(self) -> str:
        """Format as a human-readable summary."""
        return f"{self.rec.suggestion}\n    Evidence: {self.rec.evidence}"


class ProjectFormatter:
    """Formats a ProjectStats for display."""

    def __init__(self, project: ProjectStats) -> None:
        self.project = project

    def summary(self) -> str:
        """Format as a human-readable summary."""
        p = self.project
        marker = " [!]" if p.is_outlier else ""
        lines = [
            f"{p.project_name}{marker}: {p.session_count} sessions, "
            f"{p.success_rate:.0%} success, "
            f"{p.avg_tool_errors:.1f} errors/session, "
            f"{p.avg_duration_s / 60:.0f}min avg"
        ]
        if p.top_error_categories:
            lines.append(f"    Errors: {' '.join(f'{n}({c})' for n, c in p.top_error_categories[:3])}")
        if p.top_friction:
            lines.append(f"    Friction: {' '.join(f'{n}({c})' for n, c in p.top_friction[:3])}")
        return "\n".join(lines)


class DigestFormatter:
    """Formats a SessionDigest for display."""

    def __init__(self, digest: SessionDigest) -> None:
        self.digest = digest

    def summary(self) -> str:
        """Format as a human-readable summary."""
        lines: list[str] = []
        self.render_overview(lines)
        self.render_trends(lines)
        self.render_projects(lines)
        self.render_recommendations(lines)
        return "\n".join(lines)

    def to_json(self) -> str:
        """Serialize to JSON."""
        return json.dumps(attrs.asdict(self.digest), indent=2, default=str)

    def render_overview(self, lines: list[str]) -> None:
        """Render the overview section."""
        d = self.digest
        lines.append(f"Session Digest ({d.session_count} sessions)")
        lines.append("")
        lines.append(
            f"  {d.success_rate:.0%} success rate | "
            f"{d.avg_duration_s / 60:.1f}min avg | "
            f"{d.avg_tool_errors:.1f} errors/session"
        )
        lines.append(f"  Avg tokens: {d.avg_input_tokens:,.0f} in / {d.avg_output_tokens:,.0f} out")
        lines.append(f"  Avg tool calls: {d.avg_tool_calls:.1f}")
        if d.outcome_distribution:
            lines.append("")
            lines.append("Outcomes:")
            for outcome, count in sorted(d.outcome_distribution.items(), key=lambda x: x[1], reverse=True):
                pct = count / max(d.session_count, 1) * 100
                lines.append(f"  {outcome}: {count} ({pct:.0f}%)")
        if d.satisfaction_distribution:
            lines.append("")
            lines.append("Satisfaction:")
            for sat, count in sorted(d.satisfaction_distribution.items(), key=lambda x: x[1], reverse=True):
                pct = count / max(d.session_count, 1) * 100
                lines.append(f"  {sat}: {count} ({pct:.0f}%)")
        if d.top_friction:
            lines.append("")
            lines.append("Top friction:")
            for name, count in d.top_friction[:5]:
                lines.append(f"  {name}: {count}")

    def render_trends(self, lines: list[str]) -> None:
        """Render the trends section."""
        d = self.digest
        if not d.weeks:
            return
        lines.append("")
        lines.append("Trends")
        lines.append(
            f"  Success rate: {d.rolling_success_rate:.0%} avg "
            f"({arrow(d.success_rate_change)} {d.success_rate_change:+.0%})"
        )
        lines.append(
            f"  Error rate:   {d.rolling_error_rate:.1f}/session avg "
            f"({arrow(d.error_rate_change, invert=True)} {d.error_rate_change:+.1f})"
        )
        lines.append(
            f"  Duration:     {d.rolling_duration_s / 60:.0f}min avg "
            f"({arrow(d.duration_change, invert=True)} {d.duration_change / 60:+.0f}min)"
        )
        if len(d.weeks) >= 2:
            lines.append(
                f"  Success: [{sparkline([w.success_rate for w in d.weeks])}]  "
                f"Errors: [{sparkline([w.avg_errors_per_session for w in d.weeks])}]"
            )
        if d.error_category_deltas:
            lines.append("")
            lines.append("  Error category trends:")
            for cat, pct, rolling, latest_count in d.error_category_deltas:
                lines.append(
                    f"    {cat}: {arrow(pct, invert=True)} {pct:+.0%} ({rolling:.0f}/wk -> {latest_count:.0f})"
                )
        lines.append("")
        lines.append("  Weekly breakdown:")
        lines.extend(
            f"    {w.week}: {w.session_count} sessions, "
            f"{w.success_rate:.0%} success, "
            f"{w.avg_errors_per_session:.1f} errors, "
            f"{w.avg_duration_s / 60:.0f}min avg"
            for w in d.weeks
        )

    def render_projects(self, lines: list[str]) -> None:
        """Render the projects section."""
        d = self.digest
        if not d.projects:
            return
        lines.append("")
        lines.append(f"Projects ({len(d.projects)})")
        lines.extend(f"  {ProjectFormatter(p).summary()}" for p in d.projects)

    def render_recommendations(self, lines: list[str]) -> None:
        """Render the recommendations section."""
        d = self.digest
        if not d.recommendations:
            return
        lines.append("")
        lines.append("Recommendations")
        for i, rec in enumerate(d.recommendations, 1):
            lines.append(f"  {i}. {RecommendationFormatter(rec).summary()}")


class CodeflashFormatter:
    """Formats a CodeflashSession for display."""

    def __init__(self, cf: CodeflashSession) -> None:
        self.cf = cf

    def summary(self) -> str:
        """Format as a human-readable summary."""
        if not self.cf.is_codeflash:
            return "Not a codeflash session"
        c = self.cf
        lines = ["Codeflash plugin session"]
        if c.language:
            lines[0] += f" ({c.language})"
        pairs = [
            (c.optimization_domain, f"  Domain: {c.optimization_domain}"),
            (c.agents_used, f"  Agents: {', '.join(c.agents_used)}"),
            (c.skills_invoked, f"  Skills: {', '.join(c.skills_invoked)}"),
            (c.commands_invoked, f"  Commands: {', '.join(c.commands_invoked)}"),
            (c.teams_created, f"  Teams created: {c.teams_created}"),
        ]
        lines.extend(text for cond, text in pairs if cond)
        capabilities = self.capabilities()
        if capabilities:
            lines.append(f"  Capabilities: {', '.join(capabilities)}")
        return "\n".join(lines)

    def capabilities(self) -> list[str]:
        c = self.cf
        mapping = [
            (c.has_researcher, "researcher"),
            (c.has_reviewer, "reviewer"),
            (c.has_ci_handler, "CI"),
            (c.has_pr_prep, "PR prep"),
        ]
        return [name for flag, name in mapping if flag]
