#!/usr/bin/env python3
"""
Utilities for parsing and analyzing Claude Code JSONL conversation files.
"""

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple


def parse_jsonl(file_path: Path) -> List[Dict[str, Any]]:
    """Parse JSONL file and return list of message objects."""
    messages = []
    with open(file_path, "r", encoding="utf-8") as f:
        for line in f:
            if line.strip():
                messages.append(json.loads(line))
    return messages


def get_session_metadata(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    Extract session metadata from messages.

    Returns:
        Dict with keys: session_id, cwd, git_branch, version, first_timestamp
    """
    if not messages:
        return {}

    first_msg = messages[0]
    return {
        "session_id": first_msg.get("sessionId", "unknown"),
        "cwd": first_msg.get("cwd", ""),
        "git_branch": first_msg.get("gitBranch", ""),
        "version": first_msg.get("version", ""),
        "first_timestamp": first_msg.get("timestamp", ""),
    }


def extract_token_usage(message: Dict[str, Any]) -> Optional[Dict[str, int]]:
    """
    Extract token usage from an assistant message.

    Returns:
        Dict with keys: input_tokens, output_tokens, cache_creation_input_tokens,
        cache_read_input_tokens, or None if not available
    """
    if message.get("type") != "assistant":
        return None

    usage = message.get("message", {}).get("usage", {})
    if not usage:
        return None

    cache_creation = usage.get("cache_creation", {})

    return {
        "input_tokens": usage.get("input_tokens", 0),
        "output_tokens": usage.get("output_tokens", 0),
        "cache_creation_input_tokens": usage.get("cache_creation_input_tokens", 0),
        "cache_read_input_tokens": usage.get("cache_read_input_tokens", 0),
        "ephemeral_5m_input_tokens": cache_creation.get("ephemeral_5m_input_tokens", 0),
        "ephemeral_1h_input_tokens": cache_creation.get("ephemeral_1h_input_tokens", 0),
    }


def extract_model_info(message: Dict[str, Any]) -> Optional[str]:
    """Extract model name from an assistant message."""
    if message.get("type") != "assistant":
        return None
    return message.get("message", {}).get("model")


def get_tool_uses(message: Dict[str, Any]) -> List[Dict[str, Any]]:
    """Extract all tool_use blocks from an assistant message."""
    if message.get("type") != "assistant":
        return []

    content = message.get("message", {}).get("content", [])
    return [block for block in content if block.get("type") == "tool_use"]


def get_tool_results(message: Dict[str, Any]) -> List[Dict[str, Any]]:
    """Extract all tool_result blocks from a user message."""
    if message.get("type") != "user":
        return []

    content = message.get("message", {}).get("content", [])
    if isinstance(content, str):
        return []

    return [block for block in content if block.get("type") == "tool_result"]


def get_text_content(message: Dict[str, Any]) -> str:
    """Extract text content from a message."""
    msg_data = message.get("message", {})
    content = msg_data.get("content", "")

    if isinstance(content, str):
        return content

    if isinstance(content, list):
        text_blocks = [block.get("text", "") for block in content if block.get("type") == "text"]
        return "\n\n".join(text_blocks)

    return ""


def calculate_relative_time(timestamp_str: str, base_timestamp_str: str) -> str:
    """
    Calculate relative time between two ISO timestamps.

    Args:
        timestamp_str: The timestamp to compare (later time)
        base_timestamp_str: The base timestamp (earlier time)

    Returns:
        Human-readable relative time string like "5m ago", "2h ago", "3d ago"
    """
    try:
        timestamp = datetime.fromisoformat(timestamp_str.replace("Z", "+00:00"))
        base = datetime.fromisoformat(base_timestamp_str.replace("Z", "+00:00"))

        delta = timestamp - base
        total_seconds = int(delta.total_seconds())

        if total_seconds < 60:
            return "just now"
        elif total_seconds < 3600:
            minutes = total_seconds // 60
            return f"{minutes}m"
        elif total_seconds < 86400:
            hours = total_seconds // 3600
            return f"{hours}h"
        else:
            days = total_seconds // 86400
            return f"{days}d"
    except (ValueError, AttributeError):
        return ""


def get_relative_file_path(file_path: str, cwd: str) -> str:
    """
    Convert absolute file path to relative path from cwd.

    Args:
        file_path: Absolute file path
        cwd: Current working directory from session

    Returns:
        Relative path, or original path if conversion fails
    """
    try:
        abs_path = Path(file_path)
        cwd_path = Path(cwd)

        if abs_path.is_relative_to(cwd_path):
            return str(abs_path.relative_to(cwd_path))
        return file_path
    except (ValueError, AttributeError):
        return file_path


def detect_tool_success(tool_result: Dict[str, Any]) -> bool:
    """
    Detect if a tool execution was successful.

    Args:
        tool_result: Tool result block

    Returns:
        True if successful, False if error
    """
    return not tool_result.get("is_error", False)


def find_tool_use_by_id(messages: List[Dict[str, Any]], tool_use_id: str) -> Optional[Dict[str, Any]]:
    """
    Find the tool_use block that corresponds to a tool_result.

    Args:
        messages: List of all messages
        tool_use_id: The tool_use_id to search for

    Returns:
        The tool_use block, or None if not found
    """
    for message in messages:
        if message.get("type") == "assistant":
            tool_uses = get_tool_uses(message)
            for tool_use in tool_uses:
                if tool_use.get("id") == tool_use_id:
                    return tool_use
    return None


def calculate_total_tokens(messages: List[Dict[str, Any]]) -> Dict[str, int]:
    """
    Calculate total token usage across all messages.

    Returns:
        Dict with keys: total_input, total_output, total_cache_creation, total_cache_read
    """
    totals = {
        "total_input": 0,
        "total_output": 0,
        "total_cache_creation": 0,
        "total_cache_read": 0,
    }

    for message in messages:
        usage = extract_token_usage(message)
        if usage:
            totals["total_input"] += usage["input_tokens"]
            totals["total_output"] += usage["output_tokens"]
            totals["total_cache_creation"] += usage["cache_creation_input_tokens"]
            totals["total_cache_read"] += usage["cache_read_input_tokens"]

    return totals
