"""
Session tracker for Ralph loops - tracks debugging journey across iterations.

Captures:
- Session story (what was tried, what failed, what was learned)
- Git-style file change summary
- Context for spawned CLI instances

Uses workspace resolution from workspace.py.
"""

import json
from datetime import datetime
from pathlib import Path
from typing import TypedDict

from workspace import get_runtime_state_dir


class IterationRecord(TypedDict):
    """Record of a single verification iteration."""

    number: int
    approach: str  # "Tried URL-encoding the password"
    result: str  # "FAIL - broke existing tests"
    lesson: str  # "Encoding must happen after validation"
    timestamp: str
    files_changed: list[str]


class FileChange(TypedDict):
    """Record of a file change."""

    type: str  # "add", "remove", "modify"
    details: str  # "line 23: const encoded = ..."
    iteration: int


class RalphSessionState(TypedDict):
    """Complete state for a Ralph verification session."""

    session_id: str
    original_goal: str
    verification_command: str
    working_directory: str
    iterations: list[IterationRecord]
    file_changes: dict[str, list[FileChange]]
    created_at: str
    updated_at: str


def _get_ralph_sessions_dir() -> Path:
    """Get Ralph sessions directory using workspace resolution."""
    dev_fallback = Path(__file__).parent.parent.parent / "server" / "runtime-state"
    runtime_dir = get_runtime_state_dir(dev_fallback)
    return runtime_dir / "ralph-sessions"


class SessionTracker:
    """Tracks debugging session progress for Ralph loop context."""

    def __init__(self, session_id: str):
        self.session_id = session_id
        self.sessions_dir = _get_ralph_sessions_dir()
        self.state_file = self.sessions_dir / f"{session_id}.json"
        self.state = self._load_or_create()

    def _load_or_create(self) -> RalphSessionState:
        """Load existing state or create new."""
        self.sessions_dir.mkdir(parents=True, exist_ok=True)

        if self.state_file.exists():
            try:
                with open(self.state_file) as f:
                    return json.load(f)
            except (OSError, json.JSONDecodeError):
                pass

        # Create new state
        return {
            "session_id": self.session_id,
            "original_goal": "",
            "verification_command": "",
            "working_directory": "",
            "iterations": [],
            "file_changes": {},
            "created_at": datetime.now().isoformat(),
            "updated_at": datetime.now().isoformat(),
        }

    def _save(self) -> None:
        """Persist state to disk."""
        self.state["updated_at"] = datetime.now().isoformat()
        try:
            with open(self.state_file, "w") as f:
                json.dump(self.state, f, indent=2)
        except OSError:
            pass

    def set_goal(self, goal: str, verification_command: str, working_directory: str = "") -> None:
        """Set the original goal for this session."""
        self.state["original_goal"] = goal
        self.state["verification_command"] = verification_command
        self.state["working_directory"] = working_directory
        self._save()

    def record_iteration(self, approach: str, result: str, lesson: str, files_changed: list[str] | None = None) -> None:
        """Record what was tried and what was learned."""
        iteration_num = len(self.state["iterations"]) + 1
        self.state["iterations"].append(
            {
                "number": iteration_num,
                "approach": approach,
                "result": result,
                "lesson": lesson,
                "timestamp": datetime.now().isoformat(),
                "files_changed": files_changed or [],
            }
        )
        self._save()

    def record_file_change(self, file_path: str, change_type: str, details: str) -> None:
        """Track git-style changes made during session."""
        if file_path not in self.state["file_changes"]:
            self.state["file_changes"][file_path] = []

        current_iteration = len(self.state["iterations"]) + 1
        self.state["file_changes"][file_path].append(
            {"type": change_type, "details": details, "iteration": current_iteration}
        )
        self._save()

    def get_iteration_count(self) -> int:
        """Get current iteration count."""
        return len(self.state["iterations"])

    def generate_story(self) -> str:
        """Generate narrative 'session story' for spawned instance."""
        if not self.state["iterations"]:
            return "No iterations recorded yet."

        story_parts = [f"This task started with: {self.state['original_goal']}\n", "Here's what's been tried:\n"]

        for it in self.state["iterations"]:
            story_parts.append(
                f"{it['number']}. **Iteration {it['number']}**: {it['approach']}\n"
                f"   - Result: {it['result']}\n"
                f"   - Lesson: {it['lesson']}"
            )
            if it.get("files_changed"):
                story_parts.append(f"   - Files: {', '.join(it['files_changed'])}")

        return "\n\n".join(story_parts)

    def generate_diff_summary(self) -> str:
        """Generate git-style diff summary of all changes."""
        if not self.state["file_changes"]:
            return "No file changes recorded."

        lines = ["```diff", "# Files modified this session:"]

        for file_path, changes in self.state["file_changes"].items():
            lines.append(file_path)
            for change in changes:
                prefix = {"add": "+", "remove": "-", "modify": "~"}.get(change["type"], "?")
                lines.append(f"  {prefix} {change['details']}")

        lines.append("```")
        return "\n".join(lines)

    def generate_what_to_try(self) -> str:
        """Generate suggestion for next iteration based on lessons learned."""
        # Extract directory hint from verification command
        cmd = self.state.get("verification_command", "")
        dir_hint = ""
        if cmd:
            import re

            # Look for file paths in the command
            paths = re.findall(r"(/[^\s]+)", cmd)
            if paths:
                from pathlib import Path

                test_path = Path(paths[0])
                if test_path.parent.exists():
                    dir_hint = f"\n- Look in `{test_path.parent}/` for source files to fix"

        if not self.state["iterations"]:
            return f"Start by reading files in the test directory to find the bug.{dir_hint}"

        last_iteration = self.state["iterations"][-1]
        lessons = [it["lesson"] for it in self.state["iterations"]]

        # Build suggestion based on accumulated lessons
        suggestion_parts = [f"Based on the session story ({len(self.state['iterations'])} iterations so far):\n"]

        if len(lessons) >= 2:
            suggestion_parts.append(f"- Previous approaches have revealed: {'; '.join(lessons[-3:])}\n")

        suggestion_parts.append(
            f"- The last failure was: {last_iteration['result']}\n"
            f"- Consider: What pattern connects these failures?{dir_hint}"
        )

        return "".join(suggestion_parts)

    def generate_task_context(self, last_failure_output: str = "") -> str:
        """Generate complete task context for spawned CLI instance."""
        sections = []

        # Original Goal
        sections.append(f"## Original Goal\n\n{self.state['original_goal']}")

        # Session Story
        sections.append(f"## Session Story\n\n{self.generate_story()}")

        # Git-Style Change Summary
        if self.state["file_changes"]:
            sections.append(f"## Git-Style Change Summary\n\n{self.generate_diff_summary()}")

        # Current State
        changed_files = list(self.state["file_changes"].keys())
        if changed_files:
            files_list = "\n".join(f"- `{f}` ({len(self.state['file_changes'][f])} changes)" for f in changed_files[:5])
            sections.append(f"## Current State\n\nFiles to focus on:\n{files_list}")

        # Last Failure
        if last_failure_output:
            iteration_num = len(self.state["iterations"])
            sections.append(f"## Last Failure (Iteration {iteration_num})\n\n```\n{last_failure_output[:2000]}\n```")

        # What To Try Next
        sections.append(f"## What To Try Next\n\n{self.generate_what_to_try()}")

        # Instructions
        cmd = self.state["verification_command"]
        sections.append(
            "## Instructions\n\n"
            "1. Review the session story to understand what's been tried\n"
            "2. Focus on patterns in the failures\n"
            "3. Make minimal changes to fix the issue\n"
            f"4. Run `{cmd}` to verify\n"
            "5. Report: PASS or FAIL with a brief summary of what you changed"
        )

        return "\n\n".join(sections)

    def clear(self) -> None:
        """Clear session state (call on successful verification)."""
        if self.state_file.exists():
            self.state_file.unlink()


def get_session_tracker(session_id: str) -> SessionTracker:
    """Factory function to get a session tracker."""
    return SessionTracker(session_id)


def clear_ralph_session(session_id: str) -> None:
    """Clear a Ralph session by ID."""
    tracker = SessionTracker(session_id)
    tracker.clear()


def cleanup_old_ralph_sessions(max_age_hours: int = 24) -> int:
    """Delete ralph-sessions JSON files older than max_age. Returns count deleted."""
    import time

    sessions_dir = _get_ralph_sessions_dir()
    if not sessions_dir.exists():
        return 0
    cutoff = time.time() - (max_age_hours * 3600)
    count = 0
    for f in sessions_dir.glob("*.json"):
        try:
            if f.stat().st_mtime < cutoff:
                f.unlink()
                count += 1
        except OSError:
            pass
    return count
