"""Status, job list, lifecycle control and history handlers."""

from __future__ import annotations

import logging

from aiogram import F, Router
from aiogram.filters import Command, CommandObject
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message

from app.context import AppContext
from app.database.models import TERMINAL_JOB_STATUSES, Job, JobStatus
from app.errors import AppError, JobStateError
from app.handlers.common import (
    error_text,
    first_argument,
    history_text_from_jobs,
    history_text_from_videos,
    job_detail_text,
    resolve_job,
    safe_edit,
    send_job_card,
)
from app.keyboards.callbacks import JobCB
from app.keyboards.menus import (
    history_keyboard,
    job_actions_keyboard,
    jobs_list_keyboard,
    status_keyboard,
)
from app.utils.formatting import (
    JOB_STATUS_LABELS,
    format_job_summary,
    format_jobs_list,
)

router = Router(name="jobs")
logger = logging.getLogger(__name__)


async def _status_text(ctx: AppContext) -> tuple[str, Job | None]:
    counts = await ctx.repo.job_status_counts()
    total_uploaded = await ctx.repo.total_uploaded_videos()
    lines = ["<b>📊 وضعیت ربات</b>", ""]
    for status in JobStatus:
        lines.append(f"{JOB_STATUS_LABELS[status.value]}: <b>{counts.get(status.value, 0)}</b>")
    lines.append(f"ویدئوهای ارسال‌شده: <b>{total_uploaded}</b>")

    active = await ctx.repo.get_active_jobs()
    latest: Job | None = active[0] if active else None
    if latest is None:
        recent = await ctx.repo.list_jobs(limit=1)
        latest = recent[0] if recent else None
        if latest is not None:
            lines.extend(["", "<b>آخرین عملیات</b>", format_job_summary(latest, detail=False)])
    else:
        lines.extend(["", "<b>آخرین عملیات فعال</b>", format_job_summary(latest, detail=False)])
    return "\n".join(lines), latest


async def _jobs_view(
    ctx: AppContext, mode: str
) -> tuple[str, list[Job], str, bool, bool]:
    if mode == "all":
        jobs = await ctx.repo.list_jobs(limit=10)
        text = format_jobs_list(jobs, title="📚 همه عملیات‌ها")
        return text, jobs, "all", False, True
    jobs = await ctx.repo.get_active_jobs()
    if jobs:
        text = format_jobs_list(jobs, title="🗂 عملیات‌های فعال")
        return text, jobs, "active", True, False
    recent = await ctx.repo.list_jobs(limit=10)
    text = (
        "<b>🗂 عملیات‌های فعال</b>\n\nهیچ عملیات فعالی وجود ندارد.\n\n"
        + format_jobs_list(recent, title="آخرین عملیات‌ها")
    )
    return text, recent, "active", True, False


@router.message(Command("status"))
async def status_command(
    message: Message,
    command: CommandObject,
    state: FSMContext,
    ctx: AppContext,
) -> None:
    await state.clear()
    token = first_argument(command.args)
    if token:
        try:
            job = await resolve_job(ctx, token)
        except AppError as exc:
            await message.answer(f"❌ {error_text(exc)}", parse_mode="HTML")
            return
        await send_job_card(message, ctx, job)
        return
    text, latest = await _status_text(ctx)
    await message.answer(
        text,
        parse_mode="HTML",
        disable_web_page_preview=True,
        reply_markup=status_keyboard(latest),
    )


@router.message(Command("jobs"))
async def jobs_command(
    message: Message, state: FSMContext, ctx: AppContext
) -> None:
    await state.clear()
    text, jobs, mode, show_all, show_active = await _jobs_view(ctx, "active")
    await message.answer(
        text,
        parse_mode="HTML",
        disable_web_page_preview=True,
        reply_markup=jobs_list_keyboard(
            jobs,
            mode=mode,
            show_all_button=show_all,
            show_active_button=show_active,
        ),
    )


@router.message(Command("history"))
async def history_command(
    message: Message, state: FSMContext, ctx: AppContext
) -> None:
    await state.clear()
    videos = await ctx.repo.recent_uploaded(15)
    if videos:
        text = history_text_from_videos(videos)
    else:
        jobs = await ctx.repo.list_jobs(limit=10, statuses=TERMINAL_JOB_STATUSES)
        text = history_text_from_jobs(jobs)
    await message.answer(
        text,
        parse_mode="HTML",
        disable_web_page_preview=True,
        reply_markup=history_keyboard(),
    )


async def _command_lifecycle(
    message: Message,
    command: CommandObject,
    state: FSMContext,
    ctx: AppContext,
    action: str,
) -> None:
    await state.clear()
    token = first_argument(command.args)
    try:
        if action == "pause":
            job = await ctx.jobs.pause_job(await _job_id(ctx, token))
        elif action == "cancel":
            job = await ctx.jobs.cancel_job(await _job_id(ctx, token))
        else:
            if token:
                job = await ctx.jobs.resume_job(await _job_id(ctx, token))
            else:
                paused = await ctx.repo.list_jobs(limit=1, statuses=[JobStatus.PAUSED])
                if not paused:
                    raise JobStateError("هیچ عملیات متوقف‌شده‌ای برای ادامه وجود ندارد.")
                job = await ctx.jobs.resume_job(paused[0].id)
    except AppError as exc:
        await message.answer(f"❌ {error_text(exc)}", parse_mode="HTML")
        return
    except Exception:
        logger.exception("job lifecycle command failed")
        await message.answer("❌ خطای غیرمنتظره رخ داد. گزارش ربات را بررسی کنید.")
        return
    await message.answer(
        await job_detail_text(ctx, job),
        parse_mode="HTML",
        disable_web_page_preview=True,
        reply_markup=job_actions_keyboard(job),
    )


async def _job_id(ctx: AppContext, token: str | None) -> str | None:
    if not token:
        return None
    return (await resolve_job(ctx, token)).id


@router.message(Command("pause"))
async def pause_command(
    message: Message,
    command: CommandObject,
    state: FSMContext,
    ctx: AppContext,
) -> None:
    await _command_lifecycle(message, command, state, ctx, "pause")


@router.message(Command("resume"))
async def resume_command(
    message: Message,
    command: CommandObject,
    state: FSMContext,
    ctx: AppContext,
) -> None:
    await _command_lifecycle(message, command, state, ctx, "resume")


@router.message(Command("cancel"))
async def cancel_command(
    message: Message,
    command: CommandObject,
    state: FSMContext,
    ctx: AppContext,
) -> None:
    await _command_lifecycle(message, command, state, ctx, "cancel")


@router.callback_query(JobCB.filter(F.action == "status"))
async def status_callback(
    callback: CallbackQuery, callback_data: JobCB, ctx: AppContext
) -> None:
    await callback.answer()
    text, latest = await _status_text(ctx)
    await safe_edit(callback, text, status_keyboard(latest))


@router.callback_query(JobCB.filter(F.action == "list"))
async def list_callback(
    callback: CallbackQuery, callback_data: JobCB, ctx: AppContext
) -> None:
    await callback.answer()
    mode = callback_data.job_id if callback_data.job_id in {"active", "all"} else "active"
    text, jobs, mode, show_all, show_active = await _jobs_view(ctx, mode)
    await safe_edit(
        callback,
        text,
        jobs_list_keyboard(
            jobs,
            mode=mode,
            show_all_button=show_all,
            show_active_button=show_active,
        ),
    )


@router.callback_query(JobCB.filter(F.action == "view"))
async def view_callback(
    callback: CallbackQuery, callback_data: JobCB, ctx: AppContext
) -> None:
    try:
        job = await resolve_job(ctx, callback_data.job_id)
    except AppError as exc:
        await callback.answer(error_text(exc), show_alert=True)
        return
    await callback.answer()
    await safe_edit(callback, await job_detail_text(ctx, job), job_actions_keyboard(job))


@router.callback_query(JobCB.filter(F.action == "refresh"))
async def refresh_callback(
    callback: CallbackQuery, callback_data: JobCB, ctx: AppContext
) -> None:
    await view_callback(callback, callback_data, ctx)


@router.callback_query(JobCB.filter(F.action == "history"))
async def history_callback(callback: CallbackQuery, callback_data: JobCB, ctx: AppContext) -> None:
    await callback.answer()
    videos = await ctx.repo.recent_uploaded(15)
    if videos:
        text = history_text_from_videos(videos)
    else:
        jobs = await ctx.repo.list_jobs(limit=10, statuses=TERMINAL_JOB_STATUSES)
        text = history_text_from_jobs(jobs)
    await safe_edit(callback, text, history_keyboard())


_LIFECYCLE_LABELS = {
    "pause": "عملیات متوقف شد.",
    "resume": "عملیات ادامه یافت.",
    "cancel": "عملیات لغو شد.",
}


@router.callback_query(JobCB.filter(F.action.in_({"pause", "resume", "cancel"})))
async def lifecycle_callback(
    callback: CallbackQuery, callback_data: JobCB, ctx: AppContext
) -> None:
    action = callback_data.action
    try:
        if action == "pause":
            job = await ctx.jobs.pause_job(callback_data.job_id)
        elif action == "resume":
            job = await ctx.jobs.resume_job(callback_data.job_id)
        elif action == "cancel":
            job = await ctx.jobs.cancel_job(callback_data.job_id)
        else:  # pragma: no cover - filter restricts the set
            raise JobStateError("عملیات ناشناخته است.")
    except AppError as exc:
        await callback.answer(error_text(exc), show_alert=True)
        return
    except Exception:
        logger.exception("job lifecycle callback failed")
        await callback.answer("خطای غیرمنتظره رخ داد.", show_alert=True)
        return
    await callback.answer(_LIFECYCLE_LABELS[action])
    await safe_edit(callback, await job_detail_text(ctx, job), job_actions_keyboard(job))
