"""Post list, staged creation, preview, publish, edit and bulk actions."""

from __future__ import annotations

import logging

from aiogram import F, Router
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message

from app.context import AppContext
from app.database.models import (
    DestinationStatus,
    MediaStatus,
    MembershipMode,
    PostStatus,
)
from app.errors import ValidationError
from app.handlers.common import show
from app.keyboards.inline import (
    back_cancel,
    back_menu,
    bulk_confirm,
    bulk_list,
    confirm,
    newpost_media_list,
    post_channels_select,
    post_delete_select,
    post_item,
    post_list,
    post_membership_select,
    preview_actions,
)
from app.services.publishing_service import PublishResult
from app.states import BulkState, PostCreation, PostEditState, ScheduleState
from app.utils.validation import parse_duration_seconds

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

PAGE_SIZE = 8
_STATUS_LABELS = {
    "DRAFT": "پیش‌نویس",
    "READY": "آماده",
    "SCHEDULED": "زمان‌بندی‌شده",
    "PUBLISHED": "منتشرشده",
    "PAUSED": "متوقف",
    "ARCHIVED": "آرشیو",
    "FAILED": "خطا",
}
_FILTERS = {
    "all": None,
    "published": [PostStatus.PUBLISHED],
    "draft": [PostStatus.DRAFT, PostStatus.READY],
    "scheduled": [PostStatus.SCHEDULED],
    "failed": [PostStatus.FAILED],
    "archived": [PostStatus.ARCHIVED],
}


async def _media_picker(event: Message | CallbackQuery, ctx: AppContext) -> None:
    items = await ctx.repos.media.list(
        statuses=[MediaStatus.READY, MediaStatus.IN_USE], limit=8
    )
    if not items:
        await show(
            event,
            "هیچ ویدیوی آماده‌ای وجود ندارد. ابتدا در «مدیریت فیلم‌ها» گروه منبع را اضافه کنید "
            "تا ویدیوهای جدید دریافت شوند.",
            back_menu(),
        )
        return
    await show(
        event,
        "➕ ساخت پست جدید\nیک ویدیو را انتخاب کنید:",
        newpost_media_list(items),
    )


@router.callback_query(F.data == "menu:newpost")
async def newpost_home(callback: CallbackQuery, ctx: AppContext) -> None:
    await _media_picker(callback, ctx)
    await callback.answer()


@router.callback_query(F.data.startswith("post:new:"))
async def newpost_create(callback: CallbackQuery, state: FSMContext, ctx: AppContext) -> None:
    media_id = int(callback.data.rsplit(":", 1)[1])
    try:
        post = await ctx.posts.create_from_media(media_id, callback.from_user.id)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await state.clear()
    await state.update_data(post_id=post.id)
    await _prompt_title(callback, state, ctx, post.id)
    await callback.answer()


async def _prompt_title(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    await state.set_state(PostCreation.title)
    await show(
        event,
        "1️⃣ عنوان پست را بفرستید.\n"
        "برای استفاده از نام فایل یا عنوان پیش‌فرض، /skip را بفرستید.",
        back_cancel("menu:newpost"),
    )


async def _prompt_description(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    await state.set_state(PostCreation.description)
    await show(
        event,
        "2️⃣ توضیحات پست را بفرستید. برای رد کردن /skip.",
        back_cancel("menu:newpost"),
    )


async def _prompt_cover(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    await state.set_state(PostCreation.cover)
    await show(
        event,
        "3️⃣ عکس کاور را به‌صورت Photo بفرستید، یا /skip برای استفاده از کاور عمومی/تامبنیل.",
        back_cancel("menu:newpost"),
    )


async def _prompt_caption(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    await state.set_state(PostCreation.caption)
    await show(
        event,
        "4️⃣ کپشن اختصاصی این پست را بفرستید، یا /skip برای استفاده از قالب عمومی.\n"
        "متغیرهایی مثل {title}، {description} و {video_link} پشتیبانی می‌شوند.",
        back_cancel("menu:newpost"),
    )


async def _show_channels(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    channels = await ctx.repos.channels.list(active_only=True)
    if not channels:
        await state.clear()
        await show(
            event,
            "هیچ کانال انتشار فعالی وجود ندارد. ابتدا از منوی «کانال‌های انتشار» کانال اضافه کنید.",
            back_menu("menu:channels"),
        )
        return
    data = await state.get_data()
    selected = {int(item) for item in data.get("selected_channels", [])}
    if not selected:
        default = await ctx.repos.channels.default()
        if default is not None:
            selected = {default.id}
    await state.set_state(PostCreation.channels)
    await state.update_data(selected_channels=sorted(selected))
    await show(
        event,
        "5️⃣ کانال‌های مقصد را انتخاب کنید. می‌توانید چند کانال انتخاب کنید.\n"
        "برای هر کانال، ارسال جداگانه انجام می‌شود و نتیجه هر کدام ثبت می‌شود.",
        post_channels_select(channels, selected),
    )


@router.message(PostCreation.title)
async def newpost_title(message: Message, state: FSMContext, ctx: AppContext) -> None:
    if not message.text:
        await message.answer("عنوان یا /skip را بفرستید.")
        return
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    if not post_id:
        await state.clear()
        await message.answer("جلسه منقضی شده است.")
        return
    if message.text.strip() not in {"/skip", "skip"}:
        try:
            await ctx.posts.set_title(post_id, message.text)
        except Exception as exc:
            await message.answer(f"⚠️ {exc}")
            return
    await _prompt_description(message, state, ctx, post_id)


@router.message(PostCreation.description)
async def newpost_description(message: Message, state: FSMContext, ctx: AppContext) -> None:
    if not message.text:
        await message.answer("توضیحات یا /skip را بفرستید.")
        return
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    if message.text.strip() not in {"/skip", "skip"}:
        try:
            await ctx.posts.set_description(post_id, message.text)
        except Exception as exc:
            await message.answer(f"⚠️ {exc}")
            return
    await _prompt_cover(message, state, ctx, post_id)


@router.message(PostCreation.cover)
async def newpost_cover(message: Message, state: FSMContext, ctx: AppContext) -> None:
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    if message.photo:
        await ctx.posts.set_cover(post_id, message.photo[-1].file_id)
    elif message.text and message.text.strip() in {"/skip", "skip"}:
        pass
    else:
        await message.answer("عکس بفرستید یا /skip.")
        return
    await _prompt_caption(message, state, ctx, post_id)


@router.message(PostCreation.caption)
async def newpost_caption(message: Message, state: FSMContext, ctx: AppContext) -> None:
    if not message.text:
        await message.answer("کپشن یا /skip را بفرستید.")
        return
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    if message.text.strip() not in {"/skip", "skip"}:
        try:
            await ctx.posts.set_caption_override(post_id, message.text)
        except Exception as exc:
            await message.answer(f"⚠️ {exc}")
            return
    await _show_channels(message, state, ctx, post_id)


@router.callback_query(F.data.startswith("newpost:skip:"))
async def newpost_skip(callback: CallbackQuery, state: FSMContext, ctx: AppContext) -> None:
    step = callback.data.rsplit(":", 1)[1]
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    if not post_id:
        await callback.answer("جلسه منقضی شده است.", show_alert=True)
        return
    if step == "title":
        await _prompt_description(callback, state, ctx, post_id)
    elif step == "description":
        await _prompt_cover(callback, state, ctx, post_id)
    elif step == "cover":
        await _prompt_caption(callback, state, ctx, post_id)
    elif step == "caption":
        await _show_channels(callback, state, ctx, post_id)
    await callback.answer()


@router.callback_query(F.data.startswith("newpost:channel:"))
async def newpost_toggle_channel(
    callback: CallbackQuery, state: FSMContext, ctx: AppContext
) -> None:
    channel_id = int(callback.data.rsplit(":", 1)[1])
    data = await state.get_data()
    selected = {int(item) for item in data.get("selected_channels", [])}
    if channel_id in selected:
        selected.remove(channel_id)
    else:
        selected.add(channel_id)
    await state.update_data(selected_channels=sorted(selected))
    channels = await ctx.repos.channels.list(active_only=True)
    await show(
        callback,
        "5️⃣ کانال‌های مقصد را انتخاب کنید:",
        post_channels_select(channels, selected),
    )
    await callback.answer()


@router.callback_query(F.data == "newpost:channels_done")
async def newpost_channels_done(
    callback: CallbackQuery, state: FSMContext, ctx: AppContext
) -> None:
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    selected = [int(item) for item in data.get("selected_channels", [])]
    if not selected:
        await callback.answer("حداقل یک کانال انتخاب کنید.", show_alert=True)
        return
    try:
        await ctx.posts.set_destinations(post_id, selected)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await _show_membership(callback, state, ctx, post_id)
    await callback.answer()


async def _show_membership(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    data = await state.get_data()
    mode = str(data.get("membership_mode", MembershipMode.INHERIT.value))
    await state.set_state(PostCreation.membership)
    await show(
        event,
        "6️⃣ وضعیت عضویت اجباری برای این پست:\n"
        "• طبق تنظیم عمومی: کانال‌های الزامی فعال اعمال می‌شوند.\n"
        "• الزام برای این پست: همه کانال‌های الزامی فعال می‌شوند.\n"
        "• نادیده‌گرفتن: برای این پست بررسی عضویت انجام نمی‌شود.",
        post_membership_select(mode),
    )


@router.callback_query(F.data.startswith("newpost:membership:"))
async def newpost_membership_choose(
    callback: CallbackQuery, state: FSMContext
) -> None:
    mode = callback.data.rsplit(":", 1)[1]
    if mode not in {item.value for item in MembershipMode}:
        await callback.answer("گزینه نامعتبر است.", show_alert=True)
        return
    await state.update_data(membership_mode=mode)
    await show(
        callback,
        "6️⃣ وضعیت عضویت اجباری برای این پست:",
        post_membership_select(mode),
    )
    await callback.answer()


@router.callback_query(F.data == "newpost:membership_done")
async def newpost_membership_done(
    callback: CallbackQuery, state: FSMContext, ctx: AppContext
) -> None:
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    mode = str(data.get("membership_mode", MembershipMode.INHERIT.value))
    try:
        await ctx.posts.set_membership_mode(post_id, MembershipMode(mode))
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await _show_delete(callback, state, ctx, post_id)
    await callback.answer()


async def _show_delete(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    await state.set_state(PostCreation.auto_delete)
    await show(
        event,
        "7️⃣ حذف خودکار این پست:\n"
        "زمان‌ها از لحظه ارسال واقعی محاسبه می‌شوند. "
        "برای پیام کاربر و پست کانال می‌توانید زمان جداگانه انتخاب کنید.",
        post_delete_select(),
    )


@router.callback_query(F.data.startswith("newpost:delete:"))
async def newpost_delete_choose(
    callback: CallbackQuery, state: FSMContext, ctx: AppContext
) -> None:
    parts = callback.data.split(":")
    if len(parts) != 4:
        await callback.answer("گزینه نامعتبر است.", show_alert=True)
        return
    user_seconds = int(parts[2])
    channel_seconds = int(parts[3])
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    try:
        await ctx.posts.set_delete_settings(
            post_id,
            enabled=user_seconds > 0 or channel_seconds > 0,
            user_seconds=user_seconds,
            channel_seconds=channel_seconds,
        )
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await _show_preview(callback, state, ctx, post_id)
    await callback.answer()


@router.callback_query(F.data == "newpost:delete_custom")
async def newpost_delete_custom(callback: CallbackQuery, state: FSMContext) -> None:
    await state.set_state(PostCreation.auto_delete)
    await show(
        callback,
        "دو زمان را با کاما بفرستید: زمان پیام کاربر، زمان پست کانال.\n"
        "مثال: 30,300 یعنی ۳۰ ثانیه برای کاربر و ۵ دقیقه برای کانال. "
        "برای بدون حذف 0 بگذارید. حداکثر ۷ روز.",
        back_cancel("menu:newpost"),
    )
    await callback.answer()


@router.message(PostCreation.auto_delete)
async def newpost_delete_custom_receive(
    message: Message, state: FSMContext, ctx: AppContext
) -> None:
    if not message.text:
        await message.answer("دو زمان را با کاما بفرستید.")
        return
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    parts = [item.strip() for item in message.text.split(",")]
    if len(parts) != 2:
        await message.answer("قالب درست: 30,300")
        return
    try:
        values = []
        for part in parts:
            values.append(0 if part in {"0", "off", "خاموش"} else parse_duration_seconds(part))
        await ctx.posts.set_delete_settings(
            post_id,
            enabled=values[0] > 0 or values[1] > 0,
            user_seconds=values[0],
            channel_seconds=values[1],
        )
    except Exception as exc:
        await message.answer(f"⚠️ {exc}")
        return
    await _show_preview(message, state, ctx, post_id)


async def _show_preview(
    event: Message | CallbackQuery, state: FSMContext, ctx: AppContext, post_id: int
) -> None:
    try:
        preview = await ctx.posts.preview(post_id)
    except Exception as exc:
        await show(event, f"⚠️ {exc}", back_menu("menu:newpost"))
        return
    post = await ctx.repos.posts.get(post_id)
    if post is None:
        await show(event, "پست پیدا نشد.", back_menu())
        return
    policy = await ctx.app_settings.delete_policy(post)
    membership_channels = await ctx.membership.effective_for_post_model(post)
    text = (
        "👁 پیش‌نمایش پست\n\n"
        f"عنوان: {post.title or 'بدون عنوان'}\n"
        f"مقصدها: {', '.join(preview.destination_titles) or '—'}\n"
        f"وضعیت عضویت: {post.membership_mode.value}\n"
        f"کانال‌های الزامی مؤثر: {len(membership_channels)}\n"
        f"حذف خودکار: {'روشن' if policy.enabled else 'خاموش'} | "
        f"کاربر {policy.user_seconds}s | کانال {policy.channel_seconds}s\n"
        f"محافظت از محتوا: {'روشن' if await ctx.app_settings.protect_content(post) else 'خاموش'}\n"
        f"کاور: {'دارد' if preview.cover_file_id else 'ندارد'}\n"
        f"دکمه‌ها: {len(preview.buttons)}\n"
        f"لینک: {preview.video_link}\n\n"
        f"کپشن نهایی:\n{preview.caption}"
    )
    await state.set_state(PostCreation.preview)
    await show(event, text, preview_actions())


@router.callback_query(F.data == "newpost:publish")
async def newpost_publish(callback: CallbackQuery, state: FSMContext, ctx: AppContext) -> None:
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    if not post_id:
        await callback.answer("پست پیدا نشد.", show_alert=True)
        return
    await callback.answer("در حال انتشار…")
    try:
        result = await ctx.posts.publish_now(post_id, callback.from_user.id)
    except Exception as exc:
        await show(callback, f"⚠️ {exc}", back_menu(f"post:view:{post_id}"))
        return
    await state.clear()
    await show(callback, _result_text(result), back_menu(f"post:view:{post_id}"))


@router.callback_query(F.data == "newpost:save")
async def newpost_save(callback: CallbackQuery, state: FSMContext, ctx: AppContext) -> None:
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    try:
        await ctx.posts.prepare(post_id)
        await ctx.repos.posts.update(post_id, status=PostStatus.DRAFT)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await state.clear()
    await callback.answer("پیش‌نویس ذخیره شد.")
    await _render_post(callback, ctx, post_id)


@router.callback_query(F.data == "newpost:schedule")
async def newpost_schedule(callback: CallbackQuery, state: FSMContext, ctx: AppContext) -> None:
    data = await state.get_data()
    post_id = int(data.get("post_id", 0))
    await state.set_state(ScheduleState.waiting_datetime)
    await state.update_data(schedule_post_id=post_id, schedule_return="newpost")
    await show(
        callback,
        "زمان انتشار را به وقت محلی و به شکل 2026-12-31 21:30 بفرستید.\n"
        "زمان در دیتابیس به UTC ذخیره می‌شود.",
        back_cancel(f"post:view:{post_id}"),
    )
    await callback.answer()


def _result_text(result: PublishResult) -> str:
    labels = {
        DestinationStatus.PUBLISHED: "✅ منتشر شد",
        DestinationStatus.FAILED: "❌ خطا",
        DestinationStatus.UNKNOWN: "❔ نامعلوم",
        DestinationStatus.SKIPPED: "⏭ رد شد",
        DestinationStatus.SENDING: "⏳ در حال ارسال",
        DestinationStatus.PENDING: "⏳ در انتظار",
    }
    lines = [f"نتیجه انتشار پست #{result.post_id}: {result.message}", ""]
    for item in result.destinations:
        line = f"• {item.channel_title}: {labels.get(item.status, item.status.value)}"
        if item.error:
            line += f" — {item.error[:300]}"
        lines.append(line)
    return "\n".join(lines)


@router.callback_query(F.data.startswith("post:list:"))
async def post_list_view(callback: CallbackQuery, ctx: AppContext) -> None:
    try:
        _, _, filter_name, page_raw = callback.data.split(":", 3)
        page = max(0, int(page_raw))
    except (ValueError, TypeError):
        await callback.answer("درخواست نامعتبر است.", show_alert=True)
        return
    statuses = _FILTERS.get(filter_name, None)
    posts = await ctx.repos.posts.list(
        statuses=statuses, limit=PAGE_SIZE + 1, offset=page * PAGE_SIZE
    )
    has_next = len(posts) > PAGE_SIZE
    posts = posts[:PAGE_SIZE]
    total = await ctx.repos.posts.count(statuses)
    if not posts:
        await show(callback, "پستی در این دسته وجود ندارد.", back_cancel())
        await callback.answer()
        return
    lines = [f"📝 پست‌ها (صفحه {page + 1}، مجموع {total}):"]
    for post in posts:
        lines.append(
            f"• #{post.id} | {_STATUS_LABELS.get(post.status.value, post.status.value)} | "
            f"{post.title or 'بدون عنوان'}"
        )
    await show(
        callback,
        "\n".join(lines),
        post_list(posts, page=page, filter_name=filter_name, has_next=has_next),
    )
    await callback.answer()


async def _render_post(callback: CallbackQuery, ctx: AppContext, post_id: int) -> None:
    post = await ctx.repos.posts.get(post_id)
    if post is None:
        await callback.answer("پست پیدا نشد.", show_alert=True)
        return
    destinations = await ctx.repos.posts.destinations(post_id)
    active = await ctx.repos.tokens.active_for_post(post_id)
    text = (
        f"📝 پست #{post.id}\n"
        f"عنوان: {post.title or 'بدون عنوان'}\n"
        f"وضعیت: {_STATUS_LABELS.get(post.status.value, post.status.value)}\n"
        f"فایل: {post.media_asset.file_name if post.media_asset else '—'}\n"
        f"مقصدها: {len(destinations)}\n"
        f"تعداد انتشار: {post.publish_count}\n"
        f"زمان انتشار: {post.published_at or '—'}\n"
        f"زمان‌بندی: {post.scheduled_at or '—'}\n"
        f"لینک: {post.video_link or '—'}\n"
        f"توکن فعال: {'دارد' if active else 'ندارد'}\n"
        f"آخرین خطا: {post.last_error or '—'}\n"
        f"کپشن: {(post.caption_override or 'قالب عمومی')[:300]}"
    )
    await show(callback, text, post_item(post))


@router.callback_query(F.data.startswith("post:view:"))
async def post_view(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await _render_post(callback, ctx, post_id)
    await callback.answer()


@router.callback_query(F.data.startswith("post:preview:"))
async def post_preview(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    try:
        preview = await ctx.posts.preview(post_id)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    text = (
        f"👁 پیش‌نمایش پست #{post_id}\n\n"
        f"مقصدها: {', '.join(preview.destination_titles) or '—'}\n"
        f"لینک: {preview.video_link}\n"
        f"کاور: {'دارد' if preview.cover_file_id else 'ندارد'}\n"
        f"دکمه‌ها: {len(preview.buttons)}\n\n"
        f"کپشن:\n{preview.caption}"
    )
    await show(callback, text, back_menu(f"post:view:{post_id}"))
    await callback.answer()


@router.callback_query(F.data.startswith("post:publish:"))
async def post_publish_ask(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await show(
        callback,
        "این پست در همه مقصدهای انتخاب‌شده منتشر شود؟\n"
        "برای جلوگیری از ارسال تکراری، مقصدهایی که قبلاً منتشر شده‌اند دوباره ارسال نمی‌شوند.",
        confirm("publish", post_id, back=f"post:view:{post_id}"),
    )
    await callback.answer()


@router.callback_query(F.data.startswith("confirm:publish:"))
async def post_publish_confirm(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await callback.answer("در حال انتشار…")
    try:
        result = await ctx.posts.publish_now(post_id, callback.from_user.id)
    except Exception as exc:
        await show(callback, f"⚠️ {exc}", back_menu(f"post:view:{post_id}"))
        return
    await show(callback, _result_text(result), back_menu(f"post:view:{post_id}"))


@router.callback_query(F.data.startswith("post:republish:"))
async def post_republish_ask(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await show(
        callback,
        "انتشار مجدد یک نسخه تازه در کانال‌ها ارسال می‌کند و پیام قبلی را تغییر نمی‌دهد. "
        "فقط اگر از ارسال تکراری مطمئن هستید ادامه دهید.",
        confirm("republish", post_id, back=f"post:view:{post_id}"),
    )
    await callback.answer()


@router.callback_query(F.data.startswith("confirm:republish:"))
async def post_republish_confirm(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await callback.answer("در حال انتشار مجدد...")
    try:
        result = await ctx.posts.republish(post_id, callback.from_user.id)
    except Exception as exc:
        await show(callback, f"خطا: {exc}", back_menu(f"post:view:{post_id}"))
        return
    await show(callback, _result_text(result), back_menu(f"post:view:{post_id}"))


@router.callback_query(F.data.startswith("post:schedule:"))
async def post_schedule(callback: CallbackQuery, state: FSMContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await state.set_state(ScheduleState.waiting_datetime)
    await state.update_data(schedule_post_id=post_id, schedule_return="post")
    await show(
        callback,
        "زمان انتشار را به وقت محلی و به شکل 2026-12-31 21:30 بفرستید.\n"
        "برای لغو /cancel.",
        back_cancel(f"post:view:{post_id}"),
    )
    await callback.answer()


@router.callback_query(F.data.startswith("post:clone:"))
async def post_clone(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    try:
        clone = await ctx.posts.clone(post_id, callback.from_user.id)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await callback.answer("کپی ساخته شد.")
    await _render_post(callback, ctx, clone.id)


@router.callback_query(F.data.startswith("post:destinations:"))
async def post_destinations(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    destinations = await ctx.repos.posts.destinations(post_id)
    if not destinations:
        await show(callback, "مقصدی ثبت نشده است.", back_menu(f"post:view:{post_id}"))
        await callback.answer()
        return
    lines = [f"📢 مقصدهای پست #{post_id}:"]
    for item in destinations:
        lines.append(
            f"• {item.channel.title or item.channel.chat_id}: {item.status.value}"
            f"{' | پیام ' + str(item.telegram_message_id) if item.telegram_message_id else ''}"
            f"{' | ' + item.error_message if item.error_message else ''}"
        )
    await show(callback, "\n".join(lines), back_menu(f"post:view:{post_id}"))
    await callback.answer()


@router.callback_query(F.data.startswith("post:archive:"))
async def post_archive_ask(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await show(
        callback,
        "پست آرشیو شود؟ پیام‌های منتشرشده در کانال حذف نمی‌شوند.",
        confirm("archive", post_id, back=f"post:view:{post_id}"),
    )
    await callback.answer()


@router.callback_query(F.data.startswith("confirm:archive:"))
async def post_archive_confirm(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    try:
        await ctx.posts.archive(post_id, callback.from_user.id)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await callback.answer("آرشیو شد.")
    await _render_post(callback, ctx, post_id)


@router.callback_query(F.data.startswith("post:delete:"))
async def post_delete_ask(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await show(
        callback,
        "پست از دیتابیس حذف شود؟\n"
        "پیام‌های منتشرشده در کانال و پیام‌های ارسال‌شده به کاربران باقی می‌مانند. "
        "سوابق تحویل مرتبط با این پست حذف می‌شوند.",
        confirm("delete", post_id, back=f"post:view:{post_id}"),
    )
    await callback.answer()


@router.callback_query(F.data.startswith("confirm:delete:"))
async def post_delete_confirm(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    try:
        await ctx.posts.delete(post_id, callback.from_user.id)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await callback.answer("حذف شد.")
    await show(callback, "پست حذف شد.", back_menu("post:list:all:0"))


@router.callback_query(F.data.startswith("post:retry:"))
async def post_retry_ask(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await show(
        callback,
        "مقصدهای FAILED دوباره ارسال شوند؟ مقصدهای UNKNOWN دوباره ارسال نمی‌شوند.",
        confirm("retry", post_id, back=f"post:view:{post_id}"),
    )
    await callback.answer()


@router.callback_query(F.data.startswith("confirm:retry:"))
async def post_retry_confirm(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    await callback.answer("در حال تلاش دوباره…")
    try:
        result = await ctx.publishing.retry_failed(post_id, callback.from_user.id)
    except Exception as exc:
        await show(callback, f"⚠️ {exc}", back_menu(f"post:view:{post_id}"))
        return
    await show(callback, _result_text(result), back_menu(f"post:view:{post_id}"))


@router.callback_query(F.data.startswith("post:pause:"))
async def post_pause(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    try:
        await ctx.posts.pause(post_id, callback.from_user.id)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await callback.answer("متوقف شد.")
    await _render_post(callback, ctx, post_id)


@router.callback_query(F.data.startswith("post:resume:"))
async def post_resume(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    try:
        await ctx.posts.resume(post_id, callback.from_user.id)
    except Exception as exc:
        await callback.answer(str(exc)[:190], show_alert=True)
        return
    await callback.answer("ادامه یافت.")
    await _render_post(callback, ctx, post_id)


@router.callback_query(F.data.startswith("post:edit:"))
async def post_edit_menu(callback: CallbackQuery, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    keyboard = InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(text="عنوان", callback_data=f"post:editfield:title:{post_id}"),
                InlineKeyboardButton(text="توضیحات", callback_data=f"post:editfield:description:{post_id}"),
            ],
            [
                InlineKeyboardButton(text="کپشن", callback_data=f"post:editfield:caption:{post_id}"),
                InlineKeyboardButton(text="کاور", callback_data=f"post:editfield:cover:{post_id}"),
            ],
            [
                InlineKeyboardButton(
                    text="ابتدای کپشن", callback_data=f"post:editfield:prefix:{post_id}"
                ),
                InlineKeyboardButton(
                    text="انتهای کپشن", callback_data=f"post:editfield:suffix:{post_id}"
                ),
            ],
            [
                InlineKeyboardButton(
                    text="امضای پست", callback_data=f"post:editfield:signature:{post_id}"
                ),
                InlineKeyboardButton(
                    text="قالب لینک", callback_data=f"post:editfield:link_template:{post_id}"
                ),
            ],
            [InlineKeyboardButton(text="⬅️ بازگشت", callback_data=f"post:view:{post_id}")],
        ]
    )
    await show(
        callback,
        "کدام فیلد را ویرایش می‌کنید؟ فقط پیش‌نویس/آماده/خطادار قابل ویرایش است.",
        keyboard,
    )
    await callback.answer()


@router.callback_query(F.data.startswith("post:editfield:"))
async def post_editfield(
    callback: CallbackQuery, state: FSMContext, ctx: AppContext
) -> None:
    try:
        _, _, field, post_id_raw = callback.data.split(":", 3)
        post_id = int(post_id_raw)
    except ValueError:
        await callback.answer("درخواست نامعتبر است.", show_alert=True)
        return
    if field == "cover":
        await state.set_state(PostEditState.waiting_cover)
        await state.update_data(edit_post_id=post_id)
        await show(
            callback,
            "عکس جدید را بفرستید یا /clear برای حذف کاور.",
            back_cancel(f"post:view:{post_id}"),
        )
    else:
        await state.set_state(PostEditState.waiting_text)
        await state.update_data(edit_post_id=post_id, edit_field=field)
        hint = "مقدار جدید را بفرستید. برای پاک کردن /clear."
        if field == "link_template":
            hint = (
                "قالب لینک اختصاصی این پست را بفرستید. باید شامل {token} باشد و "
                "{bot_username} اختیاری است. برای بازگشت به قالب عمومی /clear."
            )
        await show(
            callback,
            hint,
            back_cancel(f"post:view:{post_id}"),
        )
    await callback.answer()


@router.message(PostEditState.waiting_text)
async def post_edit_text(message: Message, state: FSMContext, ctx: AppContext) -> None:
    if not message.text:
        await message.answer("متن بفرستید.")
        return
    data = await state.get_data()
    post_id = int(data.get("edit_post_id", 0))
    field = data.get("edit_field")
    value = None if message.text.strip() == "/clear" else message.text.strip()
    try:
        if field == "title":
            await ctx.posts.set_title(post_id, value)
        elif field == "description":
            await ctx.posts.set_description(post_id, value)
        elif field == "caption":
            await ctx.posts.set_caption_override(post_id, value)
        elif field == "prefix":
            await ctx.posts.set_caption_prefix_override(post_id, value)
        elif field == "suffix":
            await ctx.posts.set_caption_suffix_override(post_id, value)
        elif field == "signature":
            await ctx.posts.set_signature_override(post_id, value)
        elif field == "link_template":
            await ctx.posts.set_video_link_template_override(post_id, value)
        else:
            raise ValidationError("فیلد نامعتبر است.")
    except Exception as exc:
        await message.answer(f"⚠️ {exc}")
        return
    await state.clear()
    await message.answer("✅ ذخیره شد.", reply_markup=back_menu(f"post:view:{post_id}"))


@router.message(PostEditState.waiting_cover)
async def post_edit_cover(message: Message, state: FSMContext, ctx: AppContext) -> None:
    data = await state.get_data()
    post_id = int(data.get("edit_post_id", 0))
    if message.photo:
        await ctx.posts.set_cover(post_id, message.photo[-1].file_id)
    elif message.text and message.text.strip() == "/clear":
        await ctx.posts.set_cover(post_id, None)
    else:
        await message.answer("عکس یا /clear را بفرستید.")
        return
    await state.clear()
    await message.answer("✅ کاور ذخیره شد.", reply_markup=back_menu(f"post:view:{post_id}"))


@router.callback_query(F.data == "bulk:start")
async def bulk_start(callback: CallbackQuery, state: FSMContext, ctx: AppContext) -> None:
    await state.set_state(BulkState.selecting)
    await state.update_data(bulk_selected=[])
    posts = await ctx.repos.posts.list(limit=10)
    if not posts:
        await show(callback, "پستی برای عملیات گروهی وجود ندارد.", back_menu())
        await callback.answer()
        return
    await show(
        callback,
        "⚡ عملیات گروهی\nپست‌ها را انتخاب کنید (حداکثر ۲۰ مورد):",
        bulk_list(posts, set()),
    )
    await callback.answer()


@router.callback_query(F.data.startswith("bulk:toggle:"))
async def bulk_toggle(callback: CallbackQuery, state: FSMContext, ctx: AppContext) -> None:
    post_id = int(callback.data.rsplit(":", 1)[1])
    data = await state.get_data()
    selected = {int(item) for item in data.get("bulk_selected", [])}
    if post_id in selected:
        selected.remove(post_id)
    else:
        selected.add(post_id)
    await state.update_data(bulk_selected=sorted(selected))
    posts = await ctx.repos.posts.list(limit=10)
    await show(callback, "⚡ پست‌ها را انتخاب کنید:", bulk_list(posts, selected))
    await callback.answer()


@router.callback_query(F.data.startswith("bulk:action:"))
async def bulk_action(callback: CallbackQuery, state: FSMContext) -> None:
    action = callback.data.rsplit(":", 1)[1]
    data = await state.get_data()
    selected = [int(item) for item in data.get("bulk_selected", [])]
    if not selected:
        await callback.answer("حداقل یک پست انتخاب کنید.", show_alert=True)
        return
    labels = {"publish": "انتشار", "archive": "آرشیو", "delete": "حذف"}
    await show(
        callback,
        f"آیا {labels.get(action, action)} برای {len(selected)} پست انجام شود؟\n"
        "این عملیات قابل بازگشت نیست.",
        bulk_confirm(action, len(selected)),
    )
    await callback.answer()


@router.callback_query(F.data.in_({"confirm:bulk_publish", "confirm:bulk_archive", "confirm:bulk_delete"}))
async def bulk_confirm_action(
    callback: CallbackQuery, state: FSMContext, ctx: AppContext
) -> None:
    data = await state.get_data()
    selected = [int(item) for item in data.get("bulk_selected", [])]
    if not selected:
        await callback.answer("پستی انتخاب نشده است.", show_alert=True)
        return
    action = callback.data.rsplit("_", 1)[1]
    await callback.answer("در حال انجام…")
    if action == "publish":
        results = await ctx.posts.bulk_publish(selected, callback.from_user.id)
        text = "نتیجه انتشار گروهی:\n\n" + "\n\n".join(
            f"پست #{item.post_id}: {item.status.value} — {item.message[:200]}"
            for item in results
        )
    elif action == "archive":
        count = await ctx.posts.bulk_archive(selected, callback.from_user.id)
        text = f"✅ {count} پست آرشیو شد."
    else:
        count = await ctx.posts.bulk_delete(selected, callback.from_user.id)
        text = f"🗑 {count} پست حذف شد."
    await state.clear()
    await show(callback, text, back_menu())
