"""Add LemonSlice video to an existing LiveKit voice agent.

Import start_avatar from your AgentServer job. No CLI starts a paid session.
The caller supplies its already configured AgentSession and Agent, so this
example does not select an STT, LLM, TTS, VAD, or model provider.
"""

from __future__ import annotations

import asyncio
import os
from dataclasses import dataclass, field
from typing import Callable
from urllib.parse import urlparse

from livekit import agents
from livekit.agents import Agent, AgentSession
from livekit.plugins import lemonslice


@dataclass(frozen=True)
class AvatarConfig:
    image_url: str
    api_key: str = field(repr=False)
    livekit_url: str
    livekit_api_key: str = field(repr=False)
    livekit_api_secret: str = field(repr=False)
    idle_timeout: int = 60

    def __post_init__(self) -> None:
        image = urlparse(self.image_url)
        if image.scheme != "https" or not image.netloc:
            raise ValueError("AVATAR_IMAGE_URL must be a public HTTPS image URL")
        if not self.livekit_url.startswith("wss://"):
            raise ValueError("LIVEKIT_URL must use wss://")
        if not all((self.api_key, self.livekit_api_key, self.livekit_api_secret)):
            raise ValueError("Provider and LiveKit server keys are required")
        if self.idle_timeout <= 0:
            raise ValueError("Keep a positive avatar idle timeout")

    @classmethod
    def from_environment(cls) -> "AvatarConfig":
        return cls(
            image_url=os.environ["AVATAR_IMAGE_URL"],
            api_key=os.environ["LEMONSLICE_API_KEY"],
            livekit_url=os.environ["LIVEKIT_URL"],
            livekit_api_key=os.environ["LIVEKIT_API_KEY"],
            livekit_api_secret=os.environ["LIVEKIT_API_SECRET"],
        )


async def start_avatar(
    ctx: agents.JobContext,
    session: AgentSession,
    agent: Agent,
    config: AvatarConfig,
    *,
    startup_timeout: float = 30,
    avatar_factory: Callable | None = None,
):
    """Connect, attach video, start the caller's voice agent, and own cleanup.

    A returned provider session ID is not proof that the browser has rendered
    a frame. The frontend should wait for a subscribed video track or the
    provider's bot_ready event before changing its loading state.
    """
    if startup_timeout <= 0:
        raise ValueError("startup_timeout must be positive")
    await ctx.connect()
    factory = avatar_factory or lemonslice.AvatarSession
    avatar = factory(
        api_key=config.api_key,
        agent_image_url=config.image_url,
        agent_prompt="A friendly person talking naturally",
        idle_timeout=config.idle_timeout,
        edit_image=False,
    )

    closed = False

    async def close_avatar(_reason: str = "") -> None:
        nonlocal closed
        if not closed:
            await avatar.aclose()
            closed = True

    ctx.add_shutdown_callback(close_avatar)
    try:
        async with asyncio.timeout(startup_timeout):
            await avatar.start(
                session,
                room=ctx.room,
                livekit_url=config.livekit_url,
                livekit_api_key=config.livekit_api_key,
                livekit_api_secret=config.livekit_api_secret,
            )
        await session.start(agent=agent, room=ctx.room)
    except BaseException:
        try:
            await close_avatar()
        finally:
            await ctx.room.disconnect()
        raise
    return avatar
