"""CostTracker: persistent storage and aggregate reporting for agentbudget sessions."""

import json
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Any, Optional


class CostTracker:
    """Persistent storage and aggregate reporting for agentbudget sessions.

    Reads and writes session data to ~/.agentbudget/sessions.json.
    Provides aggregate reports across sessions and time ranges.

    Example:
        tracker = CostTracker()
        tracker.report()          # print report of all sessions
        tracker.report(last=7)    # print report for last 7 days
        tracker.clear()           # clear session history
    """

    _STORAGE_DIR = Path.home() / ".agentbudget"
    _STORAGE_FILE = _STORAGE_DIR / "sessions.json"
    _DOUBLE_LINE = "═" * 51
    _SINGLE_LINE = "─" * 51

    def __init__(self) -> None:
        """Initialize CostTracker and ensure storage directory exists."""
        self._storage_dir.mkdir(parents=True, exist_ok=True)

    @property
    def _storage_dir(self) -> Path:
        return self._STORAGE_DIR

    @property
    def _storage_file(self) -> Path:
        return self._STORAGE_FILE

    def _load(self) -> list[dict[str, Any]]:
        """Load all sessions from disk. Returns empty list if file doesn't exist."""
        if not self._storage_file.exists():
            return []
        try:
            with open(self._storage_file, "r", encoding="utf-8") as f:
                return json.load(f)
        except json.JSONDecodeError:
            import warnings
            warnings.warn(
                "[agentbudget] sessions.json could not be parsed. Returning empty history.",
                RuntimeWarning,
                stacklevel=2,
            )
            return []
        except OSError:
            return []

    def _save(self, sessions: list[dict[str, Any]]) -> None:
        """Write sessions list to disk."""
        with open(self._storage_file, "w", encoding="utf-8") as f:
            json.dump(sessions, f, indent=2, ensure_ascii=False)

    def save_session(self, session_data: dict[str, Any]) -> None:
        """Append a session record to persistent storage.

        Args:
            session_data: Session dict with keys: id, name, timestamp,
                budget, total_cost, total_tokens, steps.
        """
        sessions = self._load()
        sessions.append(session_data)
        self._save(sessions)

    def _filter_sessions(self, sessions: list[dict[str, Any]], last: Optional[int]) -> list[dict[str, Any]]:
        """Filter sessions by recency.

        Args:
            sessions: All sessions from storage.
            last: If given, only include sessions from the last N days.

        Returns:
            Filtered list of sessions.
        """
        if last is None:
            return sessions
        cutoff = datetime.now(timezone.utc) - timedelta(days=last)
        filtered = []
        for s in sessions:
            try:
                ts_str = s.get("timestamp", "")
                # Handle both "Z" suffix and "+00:00" suffix
                if ts_str.endswith("Z"):
                    ts_str = ts_str[:-1] + "+00:00"
                ts = datetime.fromisoformat(ts_str)
                if ts >= cutoff:
                    filtered.append(s)
            except (ValueError, AttributeError):
                filtered.append(s)  # include sessions with unparseable timestamps
        return filtered

    def report(self, last: Optional[int] = None) -> None:
        """Print an aggregate report of tracked sessions.

        Args:
            last: If provided, only include sessions from the last N days.
                For example, last=7 reports on the past week.
        """
        sessions = self._filter_sessions(self._load(), last)

        double_line = self._DOUBLE_LINE
        single_line = self._SINGLE_LINE

        header = " agentbudget — Report" + (f" (last {last} days)" if last is not None else "")
        print(double_line)
        print(header)
        print(double_line)

        if not sessions:
            print(" No sessions recorded.")
            print(double_line)
            return

        total_sessions = len(sessions)
        total_calls = sum(len(s.get("steps", [])) for s in sessions)
        total_tokens = sum(s.get("total_tokens", 0) for s in sessions)
        total_cost = sum(s.get("total_cost", 0.0) for s in sessions)

        print(f" Sessions:        {total_sessions}")
        print(f" Total calls:     {total_calls}")
        print(f" Total tokens:    {total_tokens:,}")
        print(f" Total cost:      ${total_cost:.2f}")
        print(single_line)

        # Most expensive step (by average cost per label)
        step_costs: dict[str, list[float]] = {}
        for s in sessions:
            for step in s.get("steps", []):
                label = step.get("label", "")
                cost = step.get("cost", 0.0)
                step_costs.setdefault(label, []).append(cost)

        if step_costs:
            most_expensive_label = max(step_costs, key=lambda k: sum(step_costs[k]) / len(step_costs[k]))
            avg_cost = sum(step_costs[most_expensive_label]) / len(step_costs[most_expensive_label])
            print(f' Most expensive step:   "{most_expensive_label}" (avg ${avg_cost:.4f}/run)')

        # Most used model (by call count)
        model_counts: dict[str, int] = {}
        for s in sessions:
            for step in s.get("steps", []):
                model = step.get("model", "unknown")
                model_counts[model] = model_counts.get(model, 0) + 1

        if model_counts and total_calls > 0:
            most_used_model = max(model_counts, key=lambda k: model_counts[k])
            pct = round((model_counts[most_used_model] / total_calls) * 100)
            print(f" Most used model:       {most_used_model} ({pct}% of calls)")

        print(double_line)

    def clear(self) -> None:
        """Delete all stored session history.

        Removes the sessions.json file if it exists. The storage directory
        itself is preserved.
        """
        if self._storage_file.exists():
            self._storage_file.unlink()
