from __future__ import annotations

from typing import Any

import attrs

# ---------------------------------------------------------------------------
# Dashboard models
# ---------------------------------------------------------------------------


@attrs.frozen
class LogEntry:
    """A single renderable log event."""

    timestamp: float
    source: str  # "claude", "user", "system"
    level: str  # "assistant", "tool_call", "tool_result", "status", "error", "info"
    message: str
    data: dict[str, Any] = attrs.Factory(dict)


@attrs.frozen
class SessionInfo:
    """Lightweight metadata for the sidebar session list."""

    session_id: str
    project_path: str
    project_name: str
    transcript_path: str
    started_at: float
    finished_at: float | None = None
    first_prompt: str = ""
    message_count: int = 0
    is_live: bool = False


# ---------------------------------------------------------------------------
# Analytics models
# ---------------------------------------------------------------------------

SPARK_CHARS = " _.~*"


def sparkline(values: list[float]) -> str:
    if len(values) < 2:
        return ""
    lo, hi = min(values), max(values)
    if hi == lo:
        return SPARK_CHARS[2] * len(values)
    scale = len(SPARK_CHARS) - 1
    return "".join(SPARK_CHARS[round((v - lo) / (hi - lo) * scale)] for v in values)


def arrow(delta: float, *, invert: bool = False) -> str:
    if abs(delta) < 0.05:
        return "="
    positive = delta > 0
    if invert:
        positive = not positive
    return "^" if positive else "v"


@attrs.frozen
class SessionEvent:
    timestamp: str | None
    speaker: str  # "user" | "assistant" | "system"
    text: str
    tool_name: str | None
    file_path: str | None
    command: str | None
    is_error: bool
    error_category: str | None
    attachment_type: str | None


@attrs.frozen
class SessionMeta:
    session_id: str
    project_path: str
    transcript_path: str
    start_time: float
    end_time: float
    duration_s: float
    user_messages: int
    assistant_messages: int
    tool_calls: int
    tool_counts: dict[str, int] = attrs.Factory(dict)
    tool_errors: int = 0
    tool_error_categories: dict[str, int] = attrs.Factory(dict)
    tool_error_details: tuple[tuple[str, str], ...] = ()
    input_tokens: int = 0
    output_tokens: int = 0
    cache_read_tokens: int = 0
    cache_creation_tokens: int = 0
    languages: dict[str, int] = attrs.Factory(dict)
    files_modified: int = 0
    lines_added: int = 0
    lines_removed: int = 0
    git_commits: int = 0
    git_branch: str | None = None
    user_interruptions: int = 0
    compactions: int = 0
    subagents_spawned: int = 0
    thinking_blocks: int = 0
    web_searches: int = 0
    web_fetches: int = 0
    permission_mode: str | None = None
    first_prompt: str = ""
    codeflash: CodeflashSession | None = None

    @property
    def duration_minutes(self) -> float:
        return self.duration_s / 60

    @property
    def total_tokens(self) -> int:
        return self.input_tokens + self.output_tokens

    @property
    def cache_hit_rate(self) -> float:
        total = self.input_tokens + self.cache_read_tokens + self.cache_creation_tokens
        return self.cache_read_tokens / total if total else 0.0


@attrs.frozen
class SessionAudit:
    session_id: str
    goal_categories: dict[str, int] = attrs.Factory(dict)
    outcome: str = "unclear"
    satisfaction: str = "neutral"
    friction_counts: dict[str, int] = attrs.Factory(dict)
    session_type: str = "single_task"
    user_instructions: tuple[str, ...] = ()
    summary: str = ""


@attrs.frozen
class Recommendation:
    suggestion: str
    evidence: str
    frequency: float
    source_sessions: int


@attrs.frozen
class WeekStats:
    week: str
    session_count: int
    success_rate: float
    avg_errors_per_session: float
    avg_duration_s: float
    error_category_counts: dict[str, int] = attrs.Factory(dict)


@attrs.define
class ProjectStats:
    project_path: str
    project_name: str
    session_count: int
    success_rate: float
    avg_tool_errors: float
    avg_duration_s: float
    top_error_categories: tuple[tuple[str, int], ...]
    top_friction: tuple[tuple[str, int], ...]
    is_outlier: bool = False


@attrs.frozen
class SessionDigest:
    session_count: int
    date_range: tuple[float, float]
    success_rate: float
    outcome_distribution: dict[str, int] = attrs.Factory(dict)
    satisfaction_distribution: dict[str, int] = attrs.Factory(dict)
    top_friction: tuple[tuple[str, int], ...] = ()
    avg_duration_s: float = 0.0
    avg_input_tokens: float = 0.0
    avg_output_tokens: float = 0.0
    avg_tool_calls: float = 0.0
    avg_tool_errors: float = 0.0
    weeks: tuple[WeekStats, ...] = ()
    rolling_success_rate: float = 0.0
    rolling_error_rate: float = 0.0
    rolling_duration_s: float = 0.0
    success_rate_change: float = 0.0
    error_rate_change: float = 0.0
    duration_change: float = 0.0
    error_category_deltas: tuple[tuple[str, float, float, float], ...] = ()
    projects: tuple[ProjectStats, ...] = ()
    recommendations: tuple[Recommendation, ...] = ()


# ---------------------------------------------------------------------------
# Codeflash plugin detection
# ---------------------------------------------------------------------------

CODEFLASH_AGENT_PREFIXES = frozenset(
    {
        "codeflash",
        "codeflash-python",
        "codeflash-deep",
        "codeflash-cpu",
        "codeflash-memory",
        "codeflash-async",
        "codeflash-structure",
        "codeflash-setup",
        "codeflash-scan",
        "codeflash-ci",
        "codeflash-pr-prep",
        "codeflash-researcher",
        "codeflash-review",
        "codeflash-javascript",
        "codeflash-js-deep",
        "codeflash-js-cpu",
        "codeflash-js-memory",
        "codeflash-js-async",
        "codeflash-js-structure",
        "codeflash-js-bundle",
        "codeflash-js-setup",
        "codeflash-js-scan",
        "codeflash-js-ci",
        "codeflash-js-pr-prep",
        "codeflash-java",
        "codeflash-java-deep",
        "codeflash-java-cpu",
        "codeflash-java-memory",
        "codeflash-java-async",
        "codeflash-java-structure",
        "codeflash-java-setup",
        "codeflash-java-scan",
        "codeflash-java-ci",
        "codeflash-java-pr-prep",
    }
)

CODEFLASH_SKILLS = frozenset(
    {
        "codeflash-optimize",
        "memray-profiling",
    }
)

CODEFLASH_COMMANDS = frozenset(
    {
        "codex-review",
        "codex-setup",
        "codex-status",
    }
)


@attrs.frozen
class CodeflashSession:
    """Plugin-specific metadata detected from a codeflash agent session."""

    is_codeflash: bool = False
    language: str | None = None
    agents_used: tuple[str, ...] = ()
    skills_invoked: tuple[str, ...] = ()
    commands_invoked: tuple[str, ...] = ()
    teams_created: int = 0
    optimization_domain: str | None = None
    has_researcher: bool = False
    has_reviewer: bool = False
    has_ci_handler: bool = False
    has_pr_prep: bool = False
