"""Command tracker and listener management API routes."""

import logging

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from sqlmodel import func, select
from sqlmodel.ext.asyncio.session import AsyncSession

from ..auth.dependencies import get_current_user
from ..database.engine import get_session
from ..database.models import (
    CommandConfig,
    CommandTracker,
    CommandTrackerListener,
    ServiceProvider,
    TelegramBot,
    User,
)
from .helpers import get_owned_entity

_LOGGER = logging.getLogger(__name__)

router = APIRouter(prefix="/api/command-trackers", tags=["command-trackers"])


class CommandTrackerCreate(BaseModel):
    provider_id: int
    command_config_id: int
    name: str
    icon: str = ""
    enabled: bool = True


class CommandTrackerUpdate(BaseModel):
    name: str | None = None
    icon: str | None = None
    enabled: bool | None = None
    command_config_id: int | None = None


class ListenerCreate(BaseModel):
    listener_type: str
    listener_id: int
    allowed_album_ids: list[str] | None = None


class ListenerUpdate(BaseModel):
    allowed_album_ids: list[str] | None = None


# --- Command Tracker CRUD ---


@router.get("")
async def list_command_trackers(
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """List all command trackers for the current user, with listener counts."""
    result = await session.exec(
        select(CommandTracker).where(CommandTracker.user_id == user.id)
    )
    trackers = result.all()
    return [await _tracker_response(session, t) for t in trackers]


@router.post("", status_code=status.HTTP_201_CREATED)
async def create_command_tracker(
    body: CommandTrackerCreate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Create a new command tracker."""
    # Validate provider exists and user owns it
    provider = await session.get(ServiceProvider, body.provider_id)
    if not provider or provider.user_id != user.id:
        raise HTTPException(status_code=404, detail="Provider not found")

    # Validate command config exists and is accessible (owned or system-shared)
    config = await session.get(CommandConfig, body.command_config_id)
    if not config or config.user_id not in (user.id, 0):
        raise HTTPException(status_code=404, detail="Command config not found")

    # Validate provider_type matches
    if config.provider_type != provider.type:
        raise HTTPException(
            status_code=400,
            detail=f"Provider type mismatch: provider is '{provider.type}' but command config is for '{config.provider_type}'",
        )

    tracker = CommandTracker(user_id=user.id, **body.model_dump())
    session.add(tracker)
    await session.commit()
    await session.refresh(tracker)

    # Mark affected bots dirty for debounced auto-sync
    from ..services.command_sync import mark_dirty_for_tracker
    await mark_dirty_for_tracker(tracker.id)

    return await _tracker_response(session, tracker)


@router.get("/{tracker_id}")
async def get_command_tracker(
    tracker_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Get a single command tracker with its listeners."""
    tracker = await _get_user_tracker(session, tracker_id, user.id)
    return await _tracker_response(session, tracker, include_listeners=True)


@router.put("/{tracker_id}")
async def update_command_tracker(
    tracker_id: int,
    body: CommandTrackerUpdate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Update a command tracker."""
    tracker = await _get_user_tracker(session, tracker_id, user.id)

    updates = body.model_dump(exclude_unset=True)

    # If changing command_config_id, validate accessibility and provider_type match
    if "command_config_id" in updates and updates["command_config_id"] is not None:
        config = await session.get(CommandConfig, updates["command_config_id"])
        if not config or config.user_id not in (user.id, 0):
            raise HTTPException(status_code=404, detail="Command config not found")
        provider = await session.get(ServiceProvider, tracker.provider_id)
        if provider and config.provider_type != provider.type:
            raise HTTPException(
                status_code=400,
                detail=f"Provider type mismatch: provider is '{provider.type}' but command config is for '{config.provider_type}'",
            )

    for field, value in updates.items():
        setattr(tracker, field, value)
    session.add(tracker)
    await session.commit()
    await session.refresh(tracker)

    # Mark affected bots dirty for debounced auto-sync
    from ..services.command_sync import mark_dirty_for_tracker
    await mark_dirty_for_tracker(tracker.id)

    return await _tracker_response(session, tracker)


@router.delete("/{tracker_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_command_tracker(
    tracker_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Delete a command tracker and cascade delete its listeners."""
    tracker = await _get_user_tracker(session, tracker_id, user.id)

    # Mark affected bots dirty before deleting (chain breaks after deletion)
    from ..services.command_sync import mark_dirty_for_tracker
    await mark_dirty_for_tracker(tracker.id)

    # First read the listeners we're about to delete so we can collect the
    # set of telegram_bot IDs whose polling state may need to be re-checked.
    # Then issue a single bulk DELETE instead of N per-row deletes.
    from sqlalchemy import delete as sa_delete

    result = await session.exec(
        select(CommandTrackerListener).where(
            CommandTrackerListener.command_tracker_id == tracker_id
        )
    )
    bot_ids_to_check: set[int] = {
        listener.listener_id
        for listener in result.all()
        if listener.listener_type == "telegram_bot"
    }
    await session.execute(
        sa_delete(CommandTrackerListener).where(
            CommandTrackerListener.command_tracker_id == tracker_id
        )
    )

    await session.delete(tracker)
    await session.commit()

    # Stop polling for bots that may no longer be needed
    if bot_ids_to_check:
        from ..services.telegram_poller import stop_bot_if_unused
        for bot_id in bot_ids_to_check:
            await stop_bot_if_unused(bot_id)


@router.post("/{tracker_id}/enable")
async def enable_command_tracker(
    tracker_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Enable a command tracker."""
    tracker = await _get_user_tracker(session, tracker_id, user.id)
    tracker.enabled = True
    session.add(tracker)
    await session.commit()
    await session.refresh(tracker)

    # Mark affected bots dirty for debounced auto-sync
    from ..services.command_sync import mark_dirty_for_tracker
    await mark_dirty_for_tracker(tracker.id)

    # Start polling for any telegram bot listeners
    lr = await session.exec(
        select(CommandTrackerListener).where(
            CommandTrackerListener.command_tracker_id == tracker_id,
            CommandTrackerListener.listener_type == "telegram_bot",
        )
    )
    from ..services.telegram_poller import start_bot_if_needed
    for listener in lr.all():
        await start_bot_if_needed(listener.listener_id)

    return await _tracker_response(session, tracker)


@router.post("/{tracker_id}/disable")
async def disable_command_tracker(
    tracker_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Disable a command tracker."""
    tracker = await _get_user_tracker(session, tracker_id, user.id)
    tracker.enabled = False
    session.add(tracker)
    await session.commit()
    await session.refresh(tracker)

    # Mark affected bots dirty for debounced auto-sync
    from ..services.command_sync import mark_dirty_for_tracker
    await mark_dirty_for_tracker(tracker.id)

    # Stop polling for any telegram bot listeners that are no longer needed
    lr = await session.exec(
        select(CommandTrackerListener).where(
            CommandTrackerListener.command_tracker_id == tracker_id,
            CommandTrackerListener.listener_type == "telegram_bot",
        )
    )
    from ..services.telegram_poller import stop_bot_if_unused
    for listener in lr.all():
        await stop_bot_if_unused(listener.listener_id)

    return await _tracker_response(session, tracker)


# --- Listener Management ---


@router.get("/{tracker_id}/listeners")
async def list_listeners(
    tracker_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """List all listeners for a command tracker."""
    await _get_user_tracker(session, tracker_id, user.id)
    result = await session.exec(
        select(CommandTrackerListener).where(
            CommandTrackerListener.command_tracker_id == tracker_id
        )
    )
    return [await _listener_response(session, l) for l in result.all()]


@router.post("/{tracker_id}/listeners", status_code=status.HTTP_201_CREATED)
async def add_listener(
    tracker_id: int,
    body: ListenerCreate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Add a listener to a command tracker."""
    await _get_user_tracker(session, tracker_id, user.id)

    # Validate listener exists and user owns it
    if body.listener_type == "telegram_bot":
        bot = await session.get(TelegramBot, body.listener_id)
        if not bot or bot.user_id != user.id:
            raise HTTPException(status_code=404, detail="Telegram bot not found")
    else:
        raise HTTPException(
            status_code=400,
            detail=f"Unsupported listener type: {body.listener_type}",
        )

    # Check for duplicate
    result = await session.exec(
        select(CommandTrackerListener).where(
            CommandTrackerListener.command_tracker_id == tracker_id,
            CommandTrackerListener.listener_type == body.listener_type,
            CommandTrackerListener.listener_id == body.listener_id,
        )
    )
    if result.first():
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Listener is already linked to this command tracker",
        )

    listener = CommandTrackerListener(
        command_tracker_id=tracker_id,
        listener_type=body.listener_type,
        listener_id=body.listener_id,
        allowed_album_ids=body.allowed_album_ids,
    )
    session.add(listener)
    await session.commit()
    await session.refresh(listener)

    # Start polling for this bot if needed
    if body.listener_type == "telegram_bot":
        from ..services.telegram_poller import start_bot_if_needed
        await start_bot_if_needed(body.listener_id)

        # Mark bot dirty for debounced auto-sync
        from ..services.command_sync import mark_bot_dirty
        mark_bot_dirty(body.listener_id)

    return await _listener_response(session, listener)


@router.patch("/{tracker_id}/listeners/{listener_id}")
async def update_listener(
    tracker_id: int,
    listener_id: int,
    body: ListenerUpdate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Update a listener's per-chat settings (currently just allowed_album_ids)."""
    await _get_user_tracker(session, tracker_id, user.id)
    listener = await session.get(CommandTrackerListener, listener_id)
    if not listener or listener.command_tracker_id != tracker_id:
        raise HTTPException(status_code=404, detail="Listener not found")
    # Empty list means "no albums" which is rarely useful; treat as null (inherit).
    if body.allowed_album_ids is not None and len(body.allowed_album_ids) == 0:
        listener.allowed_album_ids = None
    else:
        listener.allowed_album_ids = body.allowed_album_ids
    session.add(listener)
    await session.commit()
    await session.refresh(listener)
    return await _listener_response(session, listener)


@router.delete("/{tracker_id}/listeners/{listener_id}", status_code=status.HTTP_204_NO_CONTENT)
async def remove_listener(
    tracker_id: int,
    listener_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Remove a listener from a command tracker."""
    await _get_user_tracker(session, tracker_id, user.id)
    listener = await session.get(CommandTrackerListener, listener_id)
    if not listener or listener.command_tracker_id != tracker_id:
        raise HTTPException(status_code=404, detail="Listener not found")

    removed_type = listener.listener_type
    removed_id = listener.listener_id

    await session.delete(listener)
    await session.commit()

    # Stop polling for this bot if no longer needed
    if removed_type == "telegram_bot":
        from ..services.telegram_poller import stop_bot_if_unused
        await stop_bot_if_unused(removed_id)

        # Mark bot dirty for debounced auto-sync
        from ..services.command_sync import mark_bot_dirty
        mark_bot_dirty(removed_id)


# --- Helpers ---


async def _tracker_response(
    session: AsyncSession, t: CommandTracker, include_listeners: bool = False
) -> dict:
    """Build command tracker response."""
    # Get listener count
    result = await session.exec(
        select(func.count()).select_from(CommandTrackerListener).where(
            CommandTrackerListener.command_tracker_id == t.id
        )
    )
    listeners_count = result.one()

    resp = {
        "id": t.id,
        "user_id": t.user_id,
        "provider_id": t.provider_id,
        "command_config_id": t.command_config_id,
        "name": t.name,
        "icon": t.icon,
        "enabled": t.enabled,
        "listeners_count": listeners_count,
        "created_at": t.created_at.isoformat(),
    }

    if include_listeners:
        lr = await session.exec(
            select(CommandTrackerListener).where(
                CommandTrackerListener.command_tracker_id == t.id
            )
        )
        resp["listeners"] = [await _listener_response(session, l) for l in lr.all()]

    return resp


async def _listener_response(session: AsyncSession, l: CommandTrackerListener) -> dict:
    name = ""
    if l.listener_type == "telegram_bot":
        bot = await session.get(TelegramBot, l.listener_id)
        if bot:
            name = bot.name
    return {
        "id": l.id,
        "command_tracker_id": l.command_tracker_id,
        "listener_type": l.listener_type,
        "listener_id": l.listener_id,
        "allowed_album_ids": l.allowed_album_ids,
        "name": name,
        "created_at": l.created_at.isoformat(),
    }


async def _get_user_tracker(
    session: AsyncSession, tracker_id: int, user_id: int
) -> CommandTracker:
    return await get_owned_entity(
        session, CommandTracker, tracker_id, user_id,
        not_found_msg="Command tracker not found",
    )
