"""Agent resource setup — shared by CLI and job executor."""

import os
from collections.abc import Callable
from dataclasses import dataclass
from pathlib import Path

import structlog

from fliiq.runtime.agent.tools import ToolRegistry
from fliiq.runtime.llm.providers import BaseLLM, LLMConfig

log = structlog.get_logger()


@dataclass
class AgentResources:
    """Resources needed to run the agent (before prompt-dependent setup)."""

    llm: BaseLLM
    tools: ToolRegistry
    soul: str
    user_soul: str | None
    playbook_content: str | None
    memory_context: str | None
    skill_info: list[dict]
    project_root: Path


def resolve_llm_config() -> LLMConfig:
    """Resolve LLM config from env vars. Priority: Anthropic -> OpenAI -> Gemini.

    Raises RuntimeError if no API key found.
    """
    from fliiq.runtime.llm.providers import LLMProvider

    providers = [
        ("ANTHROPIC_API_KEY", LLMProvider.ANTHROPIC),
        ("OPENAI_API_KEY", LLMProvider.OPENAI),
        ("GEMINI_API_KEY", LLMProvider.GEMINI),
    ]

    for env_var, provider in providers:
        key = os.environ.get(env_var)
        if key:
            return LLMConfig(provider=provider, api_key=key)

    raise RuntimeError(
        "No LLM API key found. Set one of: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY"
    )


async def setup_agent_resources(
    mode: str,
    llm_config: LLMConfig,
    project_root: Path | None = None,
    ask_user_handler: Callable | None = None,
    ask_user_choice_handler: Callable | None = None,
    plan_complete_handler: Callable | None = None,
) -> AgentResources:
    """Set up agent resources (LLM, tools, soul, playbook, memory).

    Raises RuntimeError on failure (caller decides how to surface it).
    """
    from fliiq.runtime.llm.providers import create_llm
    from fliiq.runtime.planning.playbook_loader import load_soul
    from fliiq.runtime.skills.loader import discover_skills

    if project_root is None:
        from fliiq.runtime.config import resolve_fliiq_dir

        project_root = resolve_fliiq_dir().parent
    llm = create_llm(llm_config)

    # 1. Load identity
    soul, user_soul = load_soul(project_root=project_root)

    # 2. Discover skills + build tool registry
    skills = discover_skills(project_root=project_root)
    tools = ToolRegistry()

    for skill in skills.values():
        tools.register_skill(skill)

    tools.register_builtins(
        ask_user_handler=ask_user_handler,
        ask_user_choice_handler=ask_user_choice_handler,
        plan_complete_handler=plan_complete_handler,
        mode=mode,
        project_root=project_root,
    )

    # 3. Build skill info for prompt
    skill_info = [{"name": s.name, "description": s.description} for s in skills.values()]

    # 4. Load memory context (non-fatal if missing)
    memory_context = _load_memory_context(project_root)

    return AgentResources(
        llm=llm,
        tools=tools,
        soul=soul,
        user_soul=user_soul,
        playbook_content=None,  # Set after domain detection (requires prompt)
        memory_context=memory_context,
        skill_info=skill_info,
        project_root=project_root,
    )


def _load_memory_context(project_root: Path) -> str | None:
    """Load memory context from .fliiq/memory/. Returns None on any failure."""
    try:
        from fliiq.runtime.memory.manager import MemoryManager
        from fliiq.runtime.memory.retrieval import load_recent_daily

        manager = MemoryManager.from_project_root(project_root)
        mem_parts = []

        curated = manager.load_curated()
        if curated:
            mem_parts.append(f"### Long-term Memory\n{curated}")

        recent = load_recent_daily(manager, days=3)
        if recent:
            mem_parts.append(f"### Recent Activity\n{recent}")

        skill_memories = manager.list_skill_memories()
        if skill_memories:
            mem_parts.append(
                "### Available Skill Memories\n"
                "Load these on-demand with memory_read when relevant:\n"
                + "\n".join(f"- {s}" for s in skill_memories)
            )

        if mem_parts:
            return "\n\n".join(mem_parts)
    except Exception as e:
        log.debug("memory_load_skipped", error=str(e))

    return None
