"""
Lazy integration helpers for the vendored AIIQ orchestration runtime.
"""

from __future__ import annotations

import asyncio
import inspect
from dataclasses import dataclass
from pathlib import Path
from typing import Any


class AIIQIntegrationError(RuntimeError):
    """Raised when the vendored AIIQ orchestration runtime is unavailable."""


def _missing_dependency_message(module_name: str) -> str:
    return (
        "AIIQ orchestration dependencies are not available "
        f"(missing module: {module_name}). Install this package with "
        "`pip install -e .[orchestration]`."
    )


def _load_aiiq_symbols() -> dict[str, Any]:
    try:
        from ai_orchestrator.api.main import create_app
        from ai_orchestrator.config.settings import Settings
        from ai_orchestrator.schemas.task_request import TaskConstraints, TaskRequest
        from ai_orchestrator.services.openai_compat_service import OpenAICompatService
        from ai_orchestrator.services.task_service import TaskService, build_runtime_components
        from ai_orchestrator.services.task_worker import TaskWorker
    except ModuleNotFoundError as exc:
        raise AIIQIntegrationError(_missing_dependency_message(exc.name or "unknown")) from exc

    return {
        "create_app": create_app,
        "Settings": Settings,
        "TaskConstraints": TaskConstraints,
        "TaskRequest": TaskRequest,
        "TaskService": TaskService,
        "TaskWorker": TaskWorker,
        "OpenAICompatService": OpenAICompatService,
        "build_runtime_components": build_runtime_components,
    }


def _run_sync(coro: Any) -> Any:
    try:
        asyncio.get_running_loop()
    except RuntimeError:
        return asyncio.run(coro)
    raise AIIQIntegrationError(
        "A synchronous AIIQ helper was called from an active event loop. "
        "Use the async bridge methods instead."
    )


def build_aiiq_settings(**overrides: Any) -> Any:
    """Build AIIQ settings with overrides applied."""
    symbols = _load_aiiq_symbols()
    settings_type = symbols["Settings"]
    return settings_type(**overrides)


async def _shutdown_if_supported(service: Any) -> None:
    if service is None:
        return
    shutdown = getattr(service, "shutdown", None)
    if not callable(shutdown):
        return
    result = shutdown()
    if inspect.isawaitable(result):
        await result


async def _cleanup_litellm_clients() -> None:
    try:
        import litellm
        from litellm import close_litellm_async_clients
    except Exception:
        return
    try:
        await close_litellm_async_clients()
    except Exception:
        # Cleanup is best-effort and should not mask task results.
        pass
    base_handler = getattr(litellm, "base_llm_aiohttp_handler", None)
    close = getattr(base_handler, "close", None)
    if callable(close):
        try:
            result = close()
            if inspect.isawaitable(result):
                await result
        except Exception:
            # Cleanup is best-effort and should not mask task results.
            return


@dataclass
class AIIQRuntime:
    """Bundled AIIQ runtime components built from one shared configuration."""

    settings: Any
    task_service: Any
    task_worker: Any
    openai_compat_service: Any

    def create_app(self, run_embedded_worker: bool | None = None) -> Any:
        symbols = _load_aiiq_symbols()
        create_app = symbols["create_app"]
        use_worker = self.settings.run_embedded_worker if run_embedded_worker is None else run_embedded_worker
        worker = self.task_worker if use_worker else None
        return create_app(
            settings=self.settings,
            task_service=self.task_service,
            task_worker=worker,
            openai_compat_service=self.openai_compat_service,
        )

    def build_task_bridge(self) -> "AIIQTaskBridge":
        return AIIQTaskBridge(
            settings=self.settings,
            task_service=self.task_service,
            task_worker=self.task_worker,
        )

    async def shutdown(self) -> None:
        await _shutdown_if_supported(self.task_service)
        await _shutdown_if_supported(self.task_worker)
        await _shutdown_if_supported(self.openai_compat_service)
        await _cleanup_litellm_clients()

    def shutdown_sync(self) -> None:
        _run_sync(self.shutdown())


def build_aiiq_runtime(settings: Any | None = None, **settings_overrides: Any) -> AIIQRuntime:
    """Build the vendored AIIQ runtime once and expose its reusable services."""
    symbols = _load_aiiq_symbols()
    settings_obj = settings or build_aiiq_settings(**settings_overrides)
    runtime = symbols["build_runtime_components"](settings_obj)
    task_service = symbols["TaskService"](
        settings=runtime["settings"],
        store=runtime["store"],
        queue=runtime["queue"],
        task_mode_router=runtime["task_mode_router"],
        orchestrators=runtime["orchestrators"],
    )
    task_worker = symbols["TaskWorker"](
        settings=runtime["settings"],
        store=runtime["store"],
        queue=runtime["queue"],
        orchestrators=runtime["orchestrators"],
    )
    openai_compat_service = symbols["OpenAICompatService"](
        settings=runtime["settings"],
        task_service=task_service,
    )
    return AIIQRuntime(
        settings=runtime["settings"],
        task_service=task_service,
        task_worker=task_worker,
        openai_compat_service=openai_compat_service,
    )


def build_aiiq_task_service(settings: Any | None = None, **settings_overrides: Any) -> Any:
    """Build only the AIIQ task service."""
    return build_aiiq_runtime(settings=settings, **settings_overrides).task_service


def build_aiiq_openai_compat_service(
    settings: Any | None = None,
    task_service: Any | None = None,
    **settings_overrides: Any,
) -> Any:
    """Build the OpenAI-compatible AIIQ facade."""
    symbols = _load_aiiq_symbols()
    settings_obj = settings or build_aiiq_settings(**settings_overrides)
    if task_service is None:
        task_service = build_aiiq_task_service(settings=settings_obj)
    return symbols["OpenAICompatService"](
        settings=settings_obj,
        task_service=task_service,
    )


def build_aiiq_app(
    settings: Any | None = None,
    run_embedded_worker: bool | None = None,
    **settings_overrides: Any,
) -> Any:
    """Build the vendored AIIQ FastAPI application."""
    runtime = build_aiiq_runtime(settings=settings, **settings_overrides)
    return runtime.create_app(run_embedded_worker=run_embedded_worker)


@dataclass
class AIIQTaskBridge:
    """Small convenience wrapper around AIIQ task submission and worker execution."""

    settings: Any
    task_service: Any
    task_worker: Any

    def _build_request(
        self,
        *,
        goal: str,
        repo_path: Path | str,
        max_attempts: int | None = None,
        test_command: str | None = None,
        lint_command: str | None = None,
        type_check_command: str | None = None,
        security_command: str | None = None,
        build_command: str | None = None,
        task_mode: str | None = None,
        engine: str | None = None,
        sandbox_image: str | None = None,
        use_docker: bool | None = None,
        command_timeout_seconds: int | None = None,
        model_overrides: dict[str, str] | None = None,
        extra: dict[str, Any] | None = None,
    ) -> Any:
        symbols = _load_aiiq_symbols()
        constraints = symbols["TaskConstraints"](
            max_attempts=max_attempts if max_attempts is not None else self.settings.default_max_attempts,
            test_command=test_command,
            lint_command=lint_command,
            type_check_command=type_check_command,
            security_command=security_command,
            build_command=build_command,
            task_mode=task_mode,
            engine=engine,
            sandbox_image=sandbox_image,
            use_docker=use_docker,
            command_timeout_seconds=command_timeout_seconds,
            model_overrides=model_overrides or {},
            extra=extra or {},
        )
        return symbols["TaskRequest"](
            goal=goal,
            repo_path=Path(repo_path),
            constraints=constraints,
        )

    async def submit_task(self, *, goal: str, repo_path: Path | str, **constraints: Any) -> Any:
        request = self._build_request(goal=goal, repo_path=repo_path, **constraints)
        return await self.task_service.submit_task(request)

    async def run_worker_once(self) -> bool:
        return await self.task_worker.run_once()

    async def run_until_idle(self, idle_cycles: int = 1) -> None:
        await self.task_worker.run_until_idle(idle_cycles=idle_cycles)

    async def wait_for_completion(self, task_id: str, timeout_seconds: float = 10.0) -> Any:
        return await self.task_service.wait_for_completion(task_id, timeout_seconds=timeout_seconds)

    async def cleanup(self) -> None:
        await _shutdown_if_supported(self.task_service)
        await _shutdown_if_supported(self.task_worker)
        await _cleanup_litellm_clients()

    def cleanup_sync(self) -> None:
        _run_sync(self.cleanup())

    async def run_task(
        self,
        *,
        goal: str,
        repo_path: Path | str,
        timeout_seconds: float = 300.0,
        process_queue: bool = True,
        idle_cycles: int = 1,
        **constraints: Any,
    ) -> Any:
        try:
            submitted = await self.submit_task(goal=goal, repo_path=repo_path, **constraints)
            if process_queue:
                await self.run_until_idle(idle_cycles=idle_cycles)
            return await self.wait_for_completion(submitted.task_id, timeout_seconds=timeout_seconds)
        finally:
            await self.cleanup()

    def submit_task_sync(self, *, goal: str, repo_path: Path | str, **constraints: Any) -> Any:
        return _run_sync(self.submit_task(goal=goal, repo_path=repo_path, **constraints))

    def run_task_sync(
        self,
        *,
        goal: str,
        repo_path: Path | str,
        timeout_seconds: float = 300.0,
        process_queue: bool = True,
        idle_cycles: int = 1,
        **constraints: Any,
    ) -> Any:
        return _run_sync(
            self.run_task(
                goal=goal,
                repo_path=repo_path,
                timeout_seconds=timeout_seconds,
                process_queue=process_queue,
                idle_cycles=idle_cycles,
                **constraints,
            )
        )


def build_aiiq_task_bridge(settings: Any | None = None, **settings_overrides: Any) -> AIIQTaskBridge:
    """Build a task bridge backed by the vendored AIIQ runtime."""
    return build_aiiq_runtime(settings=settings, **settings_overrides).build_task_bridge()
