"""
Progressive Summary Generator - Time-based chronological regeneration.

Regenerates summaries by processing ALL memories from oldest to newest,
using time-based chunking to keep related work sessions together.

This approach naturally handles priority resolution because Claude "sees"
the history unfold - when a priority is addressed, later memories show it.
"""

import re
from datetime import datetime
from typing import Optional, List, TYPE_CHECKING

from ..utils.timestamps import utc_now_iso

if TYPE_CHECKING:
    from .summary_generator import SummaryManager


# Evolution prompt for progressive generation
PROGRESSIVE_PROMPT = '''You are evolving a project summary by processing memories chronologically.

BATCH {batch_num} of {total_batches}

CURRENT SUMMARY:
<current_summary>
{current_summary}
</current_summary>

NEXT MEMORIES (chronological order, oldest to newest):
<batch_memories>
{batch_memories}
</batch_memories>

INSTRUCTIONS:
1. Integrate these memories into the summary
2. If a memory resolves/completes something mentioned earlier, UPDATE the summary
3. REMOVE outdated priorities/issues if they're addressed by later memories
4. Keep the summary concise (~800-1000 words)
5. Focus on: tech stack, architecture, recent focus, decisions, gotchas

CRITICAL: When you see memories that complete or resolve a "Next Priority" item,
you MUST update that section to reflect the new current priorities.

<updated_summary>
Your evolved summary here
</updated_summary>'''


def parse_timestamp(ts: str) -> Optional[datetime]:
    """Parse ISO timestamp string to naive datetime (for comparison)."""
    if not ts:
        return None
    try:
        # Handle both Z suffix and +00:00 formats
        if ts.endswith('Z'):
            ts = ts[:-1] + '+00:00'
        dt = datetime.fromisoformat(ts)
        # Convert to naive datetime for safe comparison
        if dt.tzinfo is not None:
            # Replace timezone info to get naive datetime
            dt = dt.replace(tzinfo=None)
        return dt
    except (ValueError, AttributeError):
        return None


def chunk_memories_by_time(
    memories: List[dict],
    target_size: int = 30,
    gap_hours: float = 8.0
) -> List[List[dict]]:
    """
    Chunk memories by time proximity, keeping related work together.

    - Groups memories from same day/session
    - Targets ~30 memories per chunk but flexes for coherence
    - Splits on gaps > gap_hours (default 8h = different session)

    Args:
        memories: List of memories sorted oldest-first
        target_size: Target chunk size (flexible based on time gaps)
        gap_hours: Hours gap that triggers a new chunk

    Returns:
        List of memory chunks
    """
    if not memories:
        return []

    chunks = []
    current_chunk = [memories[0]]

    for i in range(1, len(memories)):
        prev_time = parse_timestamp(memories[i - 1].get("created_at", ""))
        curr_time = parse_timestamp(memories[i].get("created_at", ""))

        # Calculate gap in hours (default to 0 if timestamps missing)
        gap = 0.0
        if prev_time and curr_time:
            gap = (curr_time - prev_time).total_seconds() / 3600

        # Start new chunk if: big time gap OR chunk is full and different hour
        if gap > gap_hours or (len(current_chunk) >= target_size and gap > 1.0):
            chunks.append(current_chunk)
            current_chunk = []

        current_chunk.append(memories[i])

    # Don't forget the last chunk
    if current_chunk:
        chunks.append(current_chunk)

    return chunks


def format_memory_for_progressive(memory: dict, index: int) -> str:
    """Format a single memory for progressive prompt."""
    created_at = memory.get("created_at", "")
    mem_type = memory.get("type", "discovery")
    title = memory.get("title", "Untitled")
    fact = memory.get("fact", "")

    # Parse timestamp for display
    date_str = ""
    if created_at:
        dt = parse_timestamp(created_at)
        if dt:
            date_str = dt.strftime("%Y-%m-%d %H:%M")
        else:
            date_str = created_at[:16] if len(created_at) >= 16 else created_at

    return f"[{index}] {date_str} | {mem_type} | {title}\n    {fact}"


async def generate_progressive_summary(
    manager: "SummaryManager",
    target_chunk_size: int = 30,
    gap_hours: float = 8.0,
    model: str = "haiku",
    trigger: str = "progressive"
) -> Optional[dict]:
    """
    Generate summary by processing ALL memories progressively from oldest to newest.

    Uses time-based chunking to keep related work sessions together.
    This naturally handles priority resolution because Claude sees history unfold.

    Args:
        manager: SummaryManager instance
        target_chunk_size: Target memories per chunk (flexible)
        gap_hours: Hour gap that triggers new chunk (session boundary)
        model: Claude model to use
        trigger: Trigger label for the summary

    Returns:
        New summary data dict or None if failed
    """
    if manager.store is None:
        print(f"[Progressive:{manager.project}] No store available")
        return None

    # Get ALL memories sorted oldest-first
    all_memories = manager.store.list_all(limit=10000, offset=0, sort_order="oldest")

    if not all_memories:
        print(f"[Progressive:{manager.project}] No memories available")
        return None

    # Smart chunking by time proximity
    chunks = chunk_memories_by_time(
        all_memories,
        target_size=target_chunk_size,
        gap_hours=gap_hours
    )

    print(f"[Progressive:{manager.project}] Processing {len(all_memories)} memories in {len(chunks)} chunks")

    # Mark as pending
    current = manager.load_current()
    if current:
        current["pending"] = True
        manager.save_current(current)

    # Start with empty summary
    current_summary = "(Starting fresh - no previous summary)"

    # Process each chunk progressively
    for i, chunk in enumerate(chunks):
        # Format memories for this chunk
        chunk_text = "\n\n".join(
            format_memory_for_progressive(m, idx + 1)
            for idx, m in enumerate(chunk)
        )

        # Build the progressive prompt
        prompt = PROGRESSIVE_PROMPT.format(
            batch_num=i + 1,
            total_batches=len(chunks),
            current_summary=current_summary,
            batch_memories=chunk_text
        )

        # Call Claude to evolve the summary
        new_summary = await manager.generate_summary(
            previous_summary=None,  # We're using our own prompt format
            new_memories=[],  # Empty - we handle formatting ourselves
            model=model
        )

        # Actually, we need to call Claude directly with our custom prompt
        # Let's use the manager's internal method with our prompt
        result = await _call_claude_with_prompt(manager, prompt, model)

        if not result:
            print(f"[Progressive:{manager.project}] Failed at chunk {i + 1}/{len(chunks)}")
            return None

        current_summary = result
        print(f"[Progressive:{manager.project}] Processed chunk {i + 1}/{len(chunks)} ({len(chunk)} memories)")

    # Save current to history before updating
    if current:
        manager.add_to_history(current)

    # Get latest memory timestamp
    latest_timestamp = all_memories[-1].get("created_at", "")
    new_version = (current.get("version", 0) if current else 0) + 1
    memory_count = manager.store.count()

    new_summary_data = {
        "project": manager.project,
        "version": new_version,
        "summary": current_summary,
        "generated_at": utc_now_iso(),
        "memory_count": memory_count,
        "last_memory_timestamp": latest_timestamp,
        "pending": False,
        "trigger": trigger,
        "new_memories_count": len(all_memories)
    }

    if manager.save_current(new_summary_data):
        print(f"[Progressive:{manager.project}] Generated v{new_version} ({len(all_memories)} memories, {len(chunks)} chunks)")
        return new_summary_data

    return None


async def _call_claude_with_prompt(
    manager: "SummaryManager",
    prompt: str,
    model: str = "haiku"
) -> Optional[str]:
    """
    Call Claude CLI with a custom prompt.

    Uses the manager's internal CLI-finding logic for consistency.
    """
    import asyncio
    import subprocess
    import sys

    from cli_utils import get_subprocess_env, get_session_dir, build_claude_cli_args

    # Find Claude CLI
    claude_exe = manager._find_claude_cli()
    if not claude_exe:
        print(f"[Progressive:{manager.project}] Claude CLI not found")
        return None

    try:
        # Get dedicated session directory
        session_logs_dir = get_session_dir(manager.project, "progressive_summary")

        # Build subprocess kwargs
        kwargs = {
            "stdin": asyncio.subprocess.PIPE,
            "stdout": asyncio.subprocess.PIPE,
            "stderr": asyncio.subprocess.PIPE,
            "env": get_subprocess_env(),
            "cwd": str(session_logs_dir),
        }

        # Windows-specific flag
        if sys.platform == 'win32':
            kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW

        # Call Claude CLI
        cli_args = build_claude_cli_args("-", model=model, use_stdin=True)
        process = await asyncio.create_subprocess_exec(
            claude_exe,
            *cli_args,
            **kwargs
        )

        # Pass prompt via stdin (180s per chunk - matches working test script)
        stdout, stderr = await asyncio.wait_for(
            process.communicate(input=prompt.encode('utf-8')),
            timeout=180
        )

        if process.returncode != 0:
            print(f"[Progressive:{manager.project}] Claude CLI error: {stderr.decode()[:200]}")
            return None

        output = stdout.decode().strip()

        # Extract summary from XML tags
        match = re.search(r"<updated_summary>(.*?)</updated_summary>", output, re.DOTALL)
        if match:
            return match.group(1).strip()

        # Fallback: use raw output
        print(f"[Progressive:{manager.project}] Warning: No XML tags, using raw output")
        return output

    except asyncio.TimeoutError:
        print(f"[Progressive:{manager.project}] Claude CLI timeout")
        return None
    except Exception as e:
        print(f"[Progressive:{manager.project}] Error: {e}")
        return None
