"""
Agent Session Logic
===================

Core agent interaction functions for running autonomous coding sessions.
"""

import asyncio
from pathlib import Path
from typing import Optional

from claude_code_sdk import ClaudeSDKClient

from client import create_client
from progress import print_session_header, print_progress_summary, is_linear_initialized
from prompts import (
    get_initializer_prompt,
    get_initializer_bis_prompt,
    get_coding_prompt,
    get_coding_prompt_library,
    copy_spec_to_project,
    copy_new_spec_to_project,
)


# Configuration
AUTO_CONTINUE_DELAY_SECONDS = 3


def is_library_project(project_dir: Path) -> bool:
    """
    Detect if this is a library/type-safety project vs a full-stack web app.

    Checks app_spec.txt for keywords related to type safety, documentation, or library projects.
    """
    app_spec_path = project_dir / "app_spec.txt"
    if not app_spec_path.exists():
        return False

    try:
        spec_content = app_spec_path.read_text(encoding='utf-8').lower()

        # Keywords that indicate a library/type-safety project
        library_keywords = [
            "type safety",
            "type annotations",
            "docstrings",
            "documentation enhancement",
            "mypy",
            "library rag",
        ]

        return any(keyword in spec_content for keyword in library_keywords)
    except Exception:
        return False


async def run_agent_session(
    client: ClaudeSDKClient,
    message: str,
    project_dir: Path,
) -> tuple[str, str]:
    """
    Run a single agent session using Claude Agent SDK.

    Args:
        client: Claude SDK client
        message: The prompt to send
        project_dir: Project directory path

    Returns:
        (status, response_text) where status is:
        - "continue" if agent should continue working
        - "done" if the project appears to be complete
        - "error" if an error occurred
    """
    print("Sending prompt to Claude Agent SDK...\n")

    try:
        project_done = False
        # Send the query
        await client.query(message)

        # Collect response text and show tool use
        response_text = ""
        async for msg in client.receive_response():
            msg_type = type(msg).__name__

            # Handle AssistantMessage (text and tool use)
            if msg_type == "AssistantMessage" and hasattr(msg, "content"):
                for block in msg.content:
                    block_type = type(block).__name__

                    if block_type == "TextBlock" and hasattr(block, "text"):
                        response_text += block.text
                        print(block.text, end="", flush=True)
                        # Early guard: if the assistant declares the project feature-complete,
                        # we stop streaming and end the loop as "done" to avoid extra work.
                        if "feature-complete" in block.text.lower():
                            project_done = True
                            break
                    elif block_type == "ToolUseBlock" and hasattr(block, "name"):
                        if project_done:
                            break
                        print(f"\n[Tool: {block.name}]", flush=True)
                        if hasattr(block, "input"):
                            input_str = str(block.input)
                            if len(input_str) > 200:
                                print(f"   Input: {input_str[:200]}...", flush=True)
                            else:
                                print(f"   Input: {input_str}", flush=True)

            # Handle UserMessage (tool results)
            elif msg_type == "UserMessage" and hasattr(msg, "content"):
                if project_done:
                    # Ignore further tool results once we've determined the project is done
                    continue
                for block in msg.content:
                    block_type = type(block).__name__

                    if block_type == "ToolResultBlock":
                        result_content = getattr(block, "content", "")
                        is_error = getattr(block, "is_error", False)

                        # Check if command was blocked by security hook
                        if "blocked" in str(result_content).lower():
                            print(f"   [BLOCKED] {result_content}", flush=True)
                        elif is_error:
                            # Show errors (truncated)
                            error_str = str(result_content)[:500]
                            print(f"   [Error] {error_str}", flush=True)
                        else:
                            # Tool succeeded - just show brief confirmation
                            print("   [Done]", flush=True)

            if project_done:
                # Stop processing further streamed messages
                break

        print("\n" + "-" * 70 + "\n")

        # Detect completion signals in the assistant's response to stop the loop
        lower_text = response_text.lower()
        if (
            "feature-complete" in lower_text
            or ("all" in lower_text and "issues" in lower_text and "completed" in lower_text)
        ):
            return "done", response_text

        return "continue", response_text

    except Exception as e:
        print(f"Error during agent session: {e}")
        return "error", str(e)


async def run_autonomous_agent(
    project_dir: Path,
    model: str,
    max_iterations: Optional[int] = None,
    new_spec_filename: Optional[str] = None,
) -> None:
    """
    Run the autonomous agent loop.

    Args:
        project_dir: Directory for the project
        model: Claude model to use
        max_iterations: Maximum number of iterations (None for unlimited)
    """
    print("\n" + "=" * 70)
    print("  AUTONOMOUS CODING AGENT DEMO")
    print("=" * 70)
    print(f"\nProject directory: {project_dir}")
    print(f"Model: {model}")
    if max_iterations:
        print(f"Max iterations: {max_iterations}")
    else:
        print("Max iterations: Unlimited (will run until completion)")
    print()

    # Create project directory
    project_dir.mkdir(parents=True, exist_ok=True)

    # Check if this is a fresh start, continuation, or adding new specs
    # We use .linear_project.json as the marker for initialization
    is_first_run = not is_linear_initialized(project_dir)
    use_initializer_bis = new_spec_filename is not None and not is_first_run

    if is_first_run:
        print("Fresh start - will use initializer agent")
        print()
        print("=" * 70)
        print("  NOTE: First session may take several minutes!")
        print("  The agent is creating Linear issues (one per feature in spec).")
        print("  This may appear to hang - it's working. Watch for [Tool: ...] output.")
        print("=" * 70)
        print()
        # Copy the app spec into the project directory for the agent to read
        copy_spec_to_project(project_dir)
    elif use_initializer_bis:
        print("Adding new specifications - will use initializer bis agent")
        print()
        print("=" * 70)
        print(f"  NOTE: Adding new features from {new_spec_filename}")
        print("  The agent will create new Linear issues for the additional features.")
        print("  This may take several minutes. Watch for [Tool: ...] output.")
        print("=" * 70)
        print()
        # Copy the new spec file into the project directory
        copy_new_spec_to_project(project_dir, new_spec_filename)
        print_progress_summary(project_dir)
    else:
        print("Continuing existing project (Linear initialized)")
        print_progress_summary(project_dir)

    # Main loop
    iteration = 0

    while True:
        iteration += 1

        # Check max iterations
        if max_iterations and iteration > max_iterations:
            print(f"\nReached max iterations ({max_iterations})")
            print("To continue, run the script again without --max-iterations")
            break

        # Print session header
        is_initializer_session = is_first_run or (use_initializer_bis and iteration == 1)
        is_bis_session = use_initializer_bis and iteration == 1
        print_session_header(iteration, is_initializer_session, is_bis_session)

        # Create client (fresh context)
        client = create_client(project_dir, model)

        # Choose prompt based on session type
        if is_first_run:
            prompt = get_initializer_prompt()
            is_first_run = False  # Only use initializer once
        elif use_initializer_bis and iteration == 1:
            prompt = get_initializer_bis_prompt()
            use_initializer_bis = False  # Only use initializer bis once
        else:
            # Detect project type and use appropriate coding prompt
            if is_library_project(project_dir):
                prompt = get_coding_prompt_library()
            else:
                prompt = get_coding_prompt()

        # Run session with async context manager
        async with client:
            status, response = await run_agent_session(client, prompt, project_dir)

        # Handle status
        if status == "continue":
            print(f"\nAgent will auto-continue in {AUTO_CONTINUE_DELAY_SECONDS}s...")
            print_progress_summary(project_dir)
            await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)

        elif status == "error":
            print("\nSession encountered an error")
            print("Will retry with a fresh session...")
            await asyncio.sleep(AUTO_CONTINUE_DELAY_SECONDS)

        elif status == "done":
            print("\nAgent reports that the project is complete (all issues done).")
            print_progress_summary(project_dir)
            break

        # Small delay between sessions
        if max_iterations is None or iteration < max_iterations:
            print("\nPreparing next session...\n")
            await asyncio.sleep(1)

    # Final summary
    print("\n" + "=" * 70)
    print("  SESSION COMPLETE")
    print("=" * 70)
    print(f"\nProject directory: {project_dir}")
    print_progress_summary(project_dir)

    # Print instructions for running the generated application
    print("\n" + "-" * 70)
    print("  TO RUN THE GENERATED APPLICATION:")
    print("-" * 70)
    print(f"\n  cd {project_dir.resolve()}")
    print("  ./init.sh           # Run the setup script")
    print("  # Or manually:")
    print("  npm install && npm run dev")
    print("\n  Then open http://localhost:3000 (or check init.sh for the URL)")
    print("-" * 70)

    print("\nDone!")
