"""Agent chat endpoint with SSE streaming."""

import asyncio
import sys
import uuid
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
from typing import Optional

from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse

from ..config import get_config
from ..models.agent import AgentChatRequest, AgentMode
from ..sse.stream import SSEHandler, EventType

router = APIRouter(prefix="/agent", tags=["agent"])

# Thread pool for running blocking agent code
_executor = ThreadPoolExecutor(max_workers=4)

# Active sessions (in-memory for now)
_sessions: dict[str, dict] = {}


def _ensure_emdash_importable():
    """Ensure the emdash_core package is importable.

    This is now a no-op since we use emdash_core directly.
    """
    pass  # emdash_core is already in the package


def _run_sdk_agent(
    message: str,
    model: str,
    sse_handler: SSEHandler,
    session_id: str,
    emitter,
    plan_mode: bool = False,
):
    """Run the agent using Anthropic Agent SDK.

    This function is called when use_sdk=True and model is a Claude model.
    It uses the SDKAgentRunner which provides native support for
    Skills, MCP, and extended thinking.
    """
    import asyncio
    from ..agent.runner import SDKAgentRunner

    # Get working directory from config
    config = get_config()
    cwd = str(config.repo_root) if config.repo_root else str(Path.cwd())

    # Create SDK runner
    runner = SDKAgentRunner(
        model=model,
        cwd=cwd,
        emitter=emitter,
        plan_mode=plan_mode,
    )

    # Store session state
    _sessions[session_id] = {
        "runner": runner,
        "message_count": 1,
        "model": model,
        "plan_mode": plan_mode,
        "is_sdk": True,
    }

    # Run async agent in sync context
    async def run_async():
        response_text = ""
        async for event in runner.run(message):
            if event.get("type") == "text":
                response_text += event.get("content", "")
        return response_text

    # Run the async code
    try:
        response = asyncio.run(run_async())
        return response
    except Exception as e:
        sse_handler.emit(EventType.ERROR, {"error": str(e)})
        raise


def _run_agent_sync(
    message: str,
    model: str,
    max_iterations: int,
    sse_handler: SSEHandler,
    session_id: str,
    images: list = None,
    plan_mode: bool = False,
    use_sdk: bool = None,
):
    """Run the agent synchronously (in thread pool).

    This function runs in a background thread and emits events
    to the SSE handler for streaming to the client.

    For Claude models with use_sdk=True, uses the Anthropic Agent SDK.
    For other models, uses the standard AgentRunner with OpenAI-compatible API.
    """
    try:
        _ensure_emdash_importable()

        # Import agent components from emdash_core
        from ..agent.runner import AgentRunner, is_claude_model
        from ..agent.toolkit import AgentToolkit
        from ..agent.events import AgentEventEmitter

        # Determine if we should use SDK
        # Auto-detect based on model if not explicitly set
        if use_sdk is None:
            import os
            # Check env var for SDK preference
            sdk_enabled = os.environ.get("EMDASH_USE_SDK", "auto").lower()
            if sdk_enabled == "true":
                use_sdk = True
            elif sdk_enabled == "false":
                use_sdk = False
            else:  # "auto"
                use_sdk = is_claude_model(model)

        # Create an emitter that forwards to SSE handler
        class SSEBridgeHandler:
            """Bridges AgentEventEmitter to SSEHandler."""

            def __init__(self, sse_handler: SSEHandler):
                self._sse = sse_handler

            def handle(self, event):
                """Forward event to SSE handler."""
                self._sse.handle(event)

        # Create agent with event emitter
        emitter = AgentEventEmitter(agent_name="Emdash Code")
        emitter.add_handler(SSEBridgeHandler(sse_handler))

        # Use SDK for Claude models if enabled
        if use_sdk and is_claude_model(model):
            return _run_sdk_agent(
                message=message,
                model=model,
                sse_handler=sse_handler,
                session_id=session_id,
                emitter=emitter,
                plan_mode=plan_mode,
            )

        # Standard path: use AgentRunner with OpenAI-compatible API
        # Create toolkit with plan_mode if requested
        # When in plan mode, generate a plan file path so write_to_file is available
        plan_file_path = None
        if plan_mode:
            from pathlib import Path
            repo_root = Path.cwd()
            plan_file_path = str(repo_root / ".emdash" / "plan.md")
            # Ensure .emdash directory exists
            (repo_root / ".emdash").mkdir(exist_ok=True)

        toolkit = AgentToolkit(plan_mode=plan_mode, plan_file_path=plan_file_path)

        runner = AgentRunner(
            toolkit=toolkit,
            model=model,
            verbose=True,
            max_iterations=max_iterations,
            emitter=emitter,
        )

        # Store session state BEFORE running (so it exists even if interrupted)
        _sessions[session_id] = {
            "runner": runner,
            "message_count": 1,
            "model": model,
            "plan_mode": plan_mode,
        }

        # Convert image data if provided
        agent_images = None
        if images:
            from ..agent.providers.base import ImageContent
            agent_images = [
                ImageContent(data=img.data, format=img.format)
                for img in images
            ]

        # Run the agent
        response = runner.run(message, images=agent_images)

        return response

    except Exception as e:
        # Emit error event
        sse_handler.emit(EventType.ERROR, {
            "message": str(e),
            "details": None,
        })
        raise


async def _run_agent_async(
    request: AgentChatRequest,
    sse_handler: SSEHandler,
    session_id: str,
):
    """Run agent in thread pool and stream events."""
    config = get_config()

    # Get model from request or config
    model = request.model or config.default_model
    max_iterations = request.options.max_iterations
    plan_mode = request.options.mode == AgentMode.PLAN

    # Emit session start
    sse_handler.emit(EventType.SESSION_START, {
        "agent_name": "Emdash Code",
        "model": model,
        "session_id": session_id,
        "query": request.message,
        "mode": request.options.mode.value,
    })

    loop = asyncio.get_event_loop()

    try:
        # Run agent in thread pool
        await loop.run_in_executor(
            _executor,
            _run_agent_sync,
            request.message,
            model,
            max_iterations,
            sse_handler,
            session_id,
            request.images,
            plan_mode,
        )

        # Emit session end
        sse_handler.emit(EventType.SESSION_END, {
            "success": True,
            "session_id": session_id,
        })

    except Exception as e:
        sse_handler.emit(EventType.SESSION_END, {
            "success": False,
            "error": str(e),
            "session_id": session_id,
        })

    finally:
        sse_handler.close()


@router.post("/chat")
async def agent_chat(request: AgentChatRequest):
    """Start an agent chat session with SSE streaming.

    The response is a Server-Sent Events stream containing:
    - session_start: Initial session info
    - tool_start: When a tool begins execution
    - tool_result: When a tool completes
    - thinking: Agent reasoning messages
    - response/partial_response: Agent text output
    - clarification: When agent needs user input
    - error/warning: Error messages
    - session_end: Session completion

    Example:
        curl -N -X POST http://localhost:8765/api/agent/chat \\
            -H "Content-Type: application/json" \\
            -d '{"message": "Find authentication code"}'
    """
    # Generate or use provided session ID
    session_id = request.session_id or str(uuid.uuid4())

    # Create SSE handler
    sse_handler = SSEHandler(agent_name="Emdash Code")

    # Start agent in background
    asyncio.create_task(_run_agent_async(request, sse_handler, session_id))

    # Return SSE stream
    return StreamingResponse(
        sse_handler,
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Session-ID": session_id,
        },
    )


@router.post("/chat/{session_id}/continue")
async def continue_chat(session_id: str, request: AgentChatRequest):
    """Continue an existing chat session.

    This allows multi-turn conversations by reusing the session state.
    """
    if session_id not in _sessions:
        raise HTTPException(
            status_code=404,
            detail=f"Session {session_id} not found"
        )

    session = _sessions[session_id]
    runner = session.get("runner")
    if not runner:
        raise HTTPException(
            status_code=400,
            detail="Session has no active runner"
        )

    # Create SSE handler
    sse_handler = SSEHandler(agent_name="Emdash Code")

    async def _continue_session():
        sse_handler.emit(EventType.SESSION_START, {
            "agent_name": "Emdash Code",
            "model": session["model"],
            "session_id": session_id,
            "query": request.message,
            "continued": True,
        })

        loop = asyncio.get_event_loop()

        try:
            # Wire up SSE handler to runner's emitter for this request
            from ..agent.events import AgentEventEmitter

            class SSEBridgeHandler:
                def __init__(self, sse_handler: SSEHandler):
                    self._sse = sse_handler

                def handle(self, event):
                    self._sse.handle(event)

            # Create fresh emitter with new SSE handler
            emitter = AgentEventEmitter(agent_name="Emdash Code")
            emitter.add_handler(SSEBridgeHandler(sse_handler))
            runner.emitter = emitter

            # Continue conversation in thread pool
            await loop.run_in_executor(
                _executor,
                lambda: runner.chat(request.message),
            )

            session["message_count"] += 1

            sse_handler.emit(EventType.SESSION_END, {
                "success": True,
                "session_id": session_id,
            })

        except Exception as e:
            sse_handler.emit(EventType.ERROR, {
                "message": str(e),
            })
            sse_handler.emit(EventType.SESSION_END, {
                "success": False,
                "error": str(e),
                "session_id": session_id,
            })

        finally:
            sse_handler.close()

    asyncio.create_task(_continue_session())

    return StreamingResponse(
        sse_handler,
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Session-ID": session_id,
        },
    )


@router.get("/sessions")
async def list_sessions():
    """List active chat sessions."""
    return {
        "sessions": [
            {
                "session_id": sid,
                "model": data.get("model"),
                "message_count": data.get("message_count", 0),
            }
            for sid, data in _sessions.items()
        ]
    }


@router.delete("/sessions/{session_id}")
async def delete_session(session_id: str):
    """Delete a chat session."""
    if session_id in _sessions:
        del _sessions[session_id]
        return {"deleted": True}
    raise HTTPException(status_code=404, detail="Session not found")


@router.get("/chat/{session_id}/plan")
async def get_pending_plan(session_id: str):
    """Get the pending plan for a session, if any.

    Returns 404 if session not found, 204 if no pending plan.
    """
    if session_id not in _sessions:
        raise HTTPException(status_code=404, detail="Session not found")

    session = _sessions[session_id]
    runner = session.get("runner")

    if not runner:
        raise HTTPException(status_code=400, detail="Session has no active runner")

    pending_plan = runner.get_pending_plan()
    if not pending_plan:
        return {"has_plan": False, "plan": None}

    return {
        "has_plan": True,
        "session_id": session_id,
        "plan": pending_plan,
    }


@router.post("/chat/{session_id}/plan/approve")
async def approve_plan(session_id: str):
    """Approve the pending plan and transition to code mode.

    Returns SSE stream for the implementation phase.
    """
    if session_id not in _sessions:
        raise HTTPException(status_code=404, detail="Session not found")

    session = _sessions[session_id]
    runner = session.get("runner")

    if not runner:
        raise HTTPException(status_code=400, detail="Session has no active runner")

    if not runner.has_pending_plan():
        raise HTTPException(status_code=400, detail="No pending plan to approve")

    # Create SSE handler for streaming the implementation
    sse_handler = SSEHandler(agent_name="Emdash Code")

    async def _run_approval():
        loop = asyncio.get_event_loop()

        try:
            # Wire up SSE handler
            from ..agent.events import AgentEventEmitter

            class SSEBridgeHandler:
                def __init__(self, sse_handler: SSEHandler):
                    self._sse = sse_handler

                def handle(self, event):
                    self._sse.handle(event)

            emitter = AgentEventEmitter(agent_name="Emdash Code")
            emitter.add_handler(SSEBridgeHandler(sse_handler))
            runner.emitter = emitter

            sse_handler.emit(EventType.SESSION_START, {
                "agent_name": "Emdash Code",
                "model": session.get("model", "unknown"),
                "session_id": session_id,
                "query": "Plan approved - implementing...",
                "plan_approved": True,
            })

            # Reset cycle state for new mode
            from ..agent.tools.modes import ModeState
            ModeState.get_instance().reset_cycle()

            # Approve and run implementation
            await loop.run_in_executor(
                _executor,
                runner.approve_plan,
            )

            session["plan_mode"] = False  # Now in code mode

            sse_handler.emit(EventType.SESSION_END, {
                "success": True,
                "session_id": session_id,
            })

        except Exception as e:
            sse_handler.emit(EventType.ERROR, {"message": str(e)})
            sse_handler.emit(EventType.SESSION_END, {
                "success": False,
                "error": str(e),
                "session_id": session_id,
            })

        finally:
            sse_handler.close()

    asyncio.create_task(_run_approval())

    return StreamingResponse(
        sse_handler,
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Session-ID": session_id,
        },
    )


@router.post("/chat/{session_id}/plan/reject")
async def reject_plan(session_id: str, feedback: str = ""):
    """Reject the pending plan with feedback.

    Returns SSE stream for the revised planning phase.
    """
    if session_id not in _sessions:
        raise HTTPException(status_code=404, detail="Session not found")

    session = _sessions[session_id]
    runner = session.get("runner")

    if not runner:
        raise HTTPException(status_code=400, detail="Session has no active runner")

    if not runner.has_pending_plan():
        raise HTTPException(status_code=400, detail="No pending plan to reject")

    sse_handler = SSEHandler(agent_name="Emdash Code")

    async def _run_rejection():
        loop = asyncio.get_event_loop()

        try:
            from ..agent.events import AgentEventEmitter

            class SSEBridgeHandler:
                def __init__(self, sse_handler: SSEHandler):
                    self._sse = sse_handler

                def handle(self, event):
                    self._sse.handle(event)

            emitter = AgentEventEmitter(agent_name="Emdash Code")
            emitter.add_handler(SSEBridgeHandler(sse_handler))
            runner.emitter = emitter

            sse_handler.emit(EventType.SESSION_START, {
                "agent_name": "Emdash Code",
                "model": session.get("model", "unknown"),
                "session_id": session_id,
                "query": f"Plan rejected - revising... {feedback}",
                "plan_rejected": True,
            })

            # Reset cycle state for revision
            from ..agent.tools.modes import ModeState
            ModeState.get_instance().reset_cycle()

            # Reject and continue planning
            await loop.run_in_executor(
                _executor,
                lambda: runner.reject_plan(feedback),
            )

            sse_handler.emit(EventType.SESSION_END, {
                "success": True,
                "session_id": session_id,
            })

        except Exception as e:
            sse_handler.emit(EventType.ERROR, {"message": str(e)})
            sse_handler.emit(EventType.SESSION_END, {
                "success": False,
                "error": str(e),
                "session_id": session_id,
            })

        finally:
            sse_handler.close()

    asyncio.create_task(_run_rejection())

    return StreamingResponse(
        sse_handler,
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Session-ID": session_id,
        },
    )


@router.post("/chat/{session_id}/planmode/approve")
async def approve_plan_mode(session_id: str):
    """Approve entering plan mode.

    Called when user approves the agent's request to enter plan mode.
    Returns SSE stream for the planning phase.
    """
    if session_id not in _sessions:
        raise HTTPException(status_code=404, detail="Session not found")

    session = _sessions[session_id]
    runner = session.get("runner")

    if not runner:
        raise HTTPException(status_code=400, detail="Session has no active runner")

    # Check if plan mode was actually requested
    from ..agent.tools.modes import ModeState
    state = ModeState.get_instance()
    if not state.plan_mode_requested:
        raise HTTPException(status_code=400, detail="No pending plan mode request")

    sse_handler = SSEHandler(agent_name="Emdash Code")

    async def _run_approval():
        loop = asyncio.get_event_loop()

        try:
            from ..agent.events import AgentEventEmitter

            class SSEBridgeHandler:
                def __init__(self, sse_handler: SSEHandler):
                    self._sse = sse_handler

                def handle(self, event):
                    self._sse.handle(event)

            emitter = AgentEventEmitter(agent_name="Emdash Code")
            emitter.add_handler(SSEBridgeHandler(sse_handler))
            runner.emitter = emitter

            sse_handler.emit(EventType.SESSION_START, {
                "agent_name": "Emdash Code",
                "model": session.get("model", "unknown"),
                "session_id": session_id,
                "query": "Plan mode approved - entering plan mode...",
                "plan_mode_approved": True,
            })

            # Approve and enter plan mode
            await loop.run_in_executor(
                _executor,
                runner.approve_plan_mode,
            )

            sse_handler.emit(EventType.SESSION_END, {
                "success": True,
                "session_id": session_id,
            })

        except Exception as e:
            sse_handler.emit(EventType.ERROR, {"message": str(e)})
            sse_handler.emit(EventType.SESSION_END, {
                "success": False,
                "error": str(e),
                "session_id": session_id,
            })

        finally:
            sse_handler.close()

    asyncio.create_task(_run_approval())

    return StreamingResponse(
        sse_handler,
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Session-ID": session_id,
        },
    )


@router.post("/chat/{session_id}/clarification/answer")
async def answer_clarification(session_id: str, answer: str):
    """Answer a pending clarification question.

    Called when the user responds to a clarification question asked by the agent
    via ask_followup_question tool. This resumes the agent with the user's answer.

    Args:
        session_id: The session ID
        answer: The user's answer to the clarification question

    Returns:
        SSE stream for the agent's continued execution
    """
    if session_id not in _sessions:
        raise HTTPException(status_code=404, detail="Session not found")

    session = _sessions[session_id]
    runner = session.get("runner")

    if not runner:
        raise HTTPException(status_code=400, detail="Session has no active runner")

    sse_handler = SSEHandler(agent_name="Emdash Code")

    async def _run_answer():
        loop = asyncio.get_event_loop()

        try:
            from ..agent.events import AgentEventEmitter

            class SSEBridgeHandler:
                def __init__(self, sse_handler: SSEHandler):
                    self._sse = sse_handler

                def handle(self, event):
                    self._sse.handle(event)

            emitter = AgentEventEmitter(agent_name="Emdash Code")
            emitter.add_handler(SSEBridgeHandler(sse_handler))
            runner.emitter = emitter

            sse_handler.emit(EventType.SESSION_START, {
                "agent_name": "Emdash Code",
                "model": session.get("model", "unknown"),
                "session_id": session_id,
                "query": f"Clarification answered: {answer[:100]}...",
                "clarification_answered": True,
            })

            # Answer the clarification and resume the agent
            await loop.run_in_executor(
                _executor,
                lambda: runner.answer_clarification(answer),
            )

            sse_handler.emit(EventType.SESSION_END, {
                "success": True,
                "session_id": session_id,
            })

        except Exception as e:
            sse_handler.emit(EventType.ERROR, {"message": str(e)})
            sse_handler.emit(EventType.SESSION_END, {
                "success": False,
                "error": str(e),
                "session_id": session_id,
            })

        finally:
            sse_handler.close()

    asyncio.create_task(_run_answer())

    return StreamingResponse(
        sse_handler,
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Session-ID": session_id,
        },
    )


@router.post("/chat/{session_id}/planmode/reject")
async def reject_plan_mode(session_id: str, feedback: str = ""):
    """Reject entering plan mode.

    Called when user rejects the agent's request to enter plan mode.
    Returns SSE stream for continued code mode execution.
    """
    if session_id not in _sessions:
        raise HTTPException(status_code=404, detail="Session not found")

    session = _sessions[session_id]
    runner = session.get("runner")

    if not runner:
        raise HTTPException(status_code=400, detail="Session has no active runner")

    # Check if plan mode was actually requested
    from ..agent.tools.modes import ModeState
    state = ModeState.get_instance()
    if not state.plan_mode_requested:
        raise HTTPException(status_code=400, detail="No pending plan mode request")

    sse_handler = SSEHandler(agent_name="Emdash Code")

    async def _run_rejection():
        loop = asyncio.get_event_loop()

        try:
            from ..agent.events import AgentEventEmitter

            class SSEBridgeHandler:
                def __init__(self, sse_handler: SSEHandler):
                    self._sse = sse_handler

                def handle(self, event):
                    self._sse.handle(event)

            emitter = AgentEventEmitter(agent_name="Emdash Code")
            emitter.add_handler(SSEBridgeHandler(sse_handler))
            runner.emitter = emitter

            sse_handler.emit(EventType.SESSION_START, {
                "agent_name": "Emdash Code",
                "model": session.get("model", "unknown"),
                "session_id": session_id,
                "query": f"Plan mode rejected - continuing in code mode... {feedback}",
                "plan_mode_rejected": True,
            })

            # Reject and stay in code mode
            await loop.run_in_executor(
                _executor,
                lambda: runner.reject_plan_mode(feedback),
            )

            sse_handler.emit(EventType.SESSION_END, {
                "success": True,
                "session_id": session_id,
            })

        except Exception as e:
            sse_handler.emit(EventType.ERROR, {"message": str(e)})
            sse_handler.emit(EventType.SESSION_END, {
                "success": False,
                "error": str(e),
                "session_id": session_id,
            })

        finally:
            sse_handler.close()

    asyncio.create_task(_run_rejection())

    return StreamingResponse(
        sse_handler,
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "Connection": "keep-alive",
            "X-Session-ID": session_id,
        },
    )
