﻿import asyncio
import logging
import aiofiles
import tempfile
import time
import os

from aiogram import Router, F
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message, InlineKeyboardButton, InlineKeyboardMarkup, FSInputFile

from app.database.crud_filter import *
from app.database.crud import *

from test import *
from config import *
from app.state import *
from app.functions import *
from app.functions import _force_kill_chrome_processes
from app.keyboards import *
from app.utils.message import safe_delete_message
from app.schedule_utils import (
    DEFAULT_WORK_FROM,
    DEFAULT_WORK_TO,
    RUN_MODE_MANUAL,
    RUN_MODE_SCHEDULE,
    format_hhmm,
    format_work_days,
    is_within_window,
    normalize_work_days,
    now_in_bot_tz,
    schedule_summary,
    validate_work_window,
)

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from bs4 import BeautifulSoup
from threading import Event
from datetime import datetime, timedelta
from sqlalchemy import select, func


_log = logging.getLogger(__name__)

router = Router()  


# Словарь для преобразования типа авто (сохранённого в поле typeavto)
type_mapping = {
    "1": "Легковые",
    "2": "Мото",
    "6": "Грузовики",
    "5": "Прицепы",
    "4": "Спецтехника",
    "7": "Автобусы",
    "3": "Водный транспорт",
    "9": "Воздушный транспорт",
    "9": "Автодома"
}

regions = {
    "Київська обл.": 10,
    "Житомирська обл.": 2,
    "Сумська обл.": 8,
    "Чернігівська обл.": 6,
    "Вінницька обл.": 1,
    "Кіровоградська обл.": 16,
    "Полтавська обл.": 20,
    "Черкаська обл.": 24,
    "Волинська обл.": 18,
    "Закарпатська обл.": 22,
    "Івано-Франківська обл.": 15,
    "Львівська обл.": 5,
    "Рівненська обл.": 9,
    "Тернопільська обл.": 3,
    "Хмельницька обл.": 4,
    "Чернівецька обл.": 25,
    "Дніпропетровська обл.": 11,
    "Донецька обл.": 13,
    "Запорізька обл.": 14,
    "Луганська обл.": 17,
    "Харківська обл.": 7,
    "Миколаївська обл.": 19,
    "Одеська обл.": 12,
    "Херсонська обл.": 23
}

user_tasks = {}

# Один активный парсинг на процесс бота: Chrome/Selenium при ~1GB RAM даёт OOM и сотни зомби-процессов при параллельных потоках.
_global_parsing_task: asyncio.Task | None = None
_global_parsing_owner_id: int | None = None
_global_parsing_gate = asyncio.Lock()

# Счётчик подряд идущих auto-restart по user_id
_auto_restart_counts: dict[int, int] = {}

_PARSING_TASK_NAMES = frozenset({"Парсинг", "Парсинг DEBUG"})


def _is_parsing_task_name(name: str | None) -> bool:
    return name in _PARSING_TASK_NAMES


def _task_is_active(task_info: dict) -> bool:
    task = task_info.get("task")
    return task is not None and not task.done()


def prune_user_tasks(user_id: int) -> list:
    """Убирает завершённые задачи из списка пользователя. Возвращает активные."""
    active = [t for t in user_tasks.get(user_id, []) if _task_is_active(t)]
    if active:
        user_tasks[user_id] = active
    else:
        user_tasks.pop(user_id, None)
    return active


def is_parsing_running_for_user(user_id: int) -> bool:
    for ti in user_tasks.get(user_id, []):
        if _is_parsing_task_name(ti.get("name")) and _task_is_active(ti):
            return True
    return False


def _region_str_from_settings(region_id) -> str:
    if isinstance(region_id, list):
        if region_id:
            region_names = [name for name, reg_id in regions.items() if reg_id in region_id]
            return ", ".join(region_names) if region_names else f"ID: {', '.join(map(str, region_id))}"
        return "Все регионы"
    if region_id and region_id != 0:
        return next((name for name, reg_id in regions.items() if reg_id == region_id), f"ID: {region_id}")
    return "Все регионы"


async def stop_parsing_for_user(user_id: int, *, source: str = "manual") -> bool:
    """Остановка парсинга: stop_event + принудительное убийство Chrome (прерывает driver.get)."""
    ut = user_tasks.get(user_id, [])
    stopped = False
    for task_info in ut:
        if not _is_parsing_task_name(task_info.get("name")):
            continue
        task = task_info.get("task")
        stop_event = task_info.get("stop_event")
        if task is not None and not task.done():
            if stop_event:
                stop_event.set()
            stopped = True
            _log.info("stop_parsing_for_user user_id=%s source=%s", user_id, source)
    if stopped:
        # Ручной/schedule стоп — не рестартить
        if not str(source).startswith("auto_restart"):
            _auto_restart_counts.pop(int(user_id), None)
        # Без kill Selenium может висеть на driver.get до 120с
        try:
            await asyncio.to_thread(_force_kill_chrome_processes)
        except Exception:
            _log.exception("Не удалось убить Chrome при стопе user_id=%s", user_id)
        # Не спамим админа на промежуточный stop при restart_if_running
        if ":restart" not in str(source):
            try:
                from app.notify import notify_admin

                await notify_admin(
                    f"⏹ Парсинг остановлен\nuser_id={user_id}\nsource={source}"
                )
            except Exception:
                _log.debug("notify_admin on stop failed", exc_info=True)
    return stopped


async def start_parsing_for_user(
    user_id: int,
    *,
    source: str = "manual",
    restart_if_running: bool = False,
    debug_mode: bool = False,
) -> str:
    """
    Запуск парсинга для user_id.
    Возвращает: started | restarted | already_running | busy_other | no_filter | stop_timeout

    debug_mode=True — парсинг без SMS, отчёт по объявлениям в Telegram.
    """
    global _global_parsing_task, _global_parsing_owner_id

    task_name = "Парсинг DEBUG" if debug_mode else "Парсинг"

    if is_parsing_running_for_user(user_id):
        if not restart_if_running:
            return "already_running"
        await stop_parsing_for_user(user_id, source=f"{source}:restart")
        t = _global_parsing_task
        if t is not None and not t.done():
            try:
                await asyncio.wait_for(asyncio.shield(t), timeout=120.0)
            except asyncio.TimeoutError:
                _log.warning("Таймаут ожидания остановки парсинга user_id=%s", user_id)
                return "stop_timeout"
        user_tasks[user_id] = [
            x for x in user_tasks.get(user_id, []) if not _is_parsing_task_name(x.get("name"))
        ]
        status_after_stop = "restarted"
    else:
        status_after_stop = "started"

    async with _global_parsing_gate:
        if _global_parsing_task is not None and not _global_parsing_task.done():
            _log.info(
                "Парсинг не запущен: занят Chrome (user_id=%s, source=%s)",
                user_id,
                source,
            )
            return "busy_other"

    filter_settings = await get_filter_settings_dict(user_id)
    if not filter_settings:
        _log.warning("Парсинг не стартовал: нет filterUser для user_id=%s", user_id)
        return "no_filter"

    # Свежий ручной/schedule старт сбрасывает счётчик auto-restart
    if source != "auto_restart":
        _auto_restart_counts.pop(int(user_id), None)

    stop_event = Event()
    main_loop = asyncio.get_running_loop()

    async def _parsing_worker():
        global _global_parsing_task, _global_parsing_owner_id
        crashed = False
        try:
            await asyncio.to_thread(
                do_parsing,
                filter_settings,
                stop_event,
                bot,
                user_id,
                main_loop,
                debug_mode,
            )
        except Exception:
            crashed = True
            _log.exception(
                "Парсинг упал (user_id=%s debug=%s source=%s)",
                user_id,
                debug_mode,
                source,
            )
        finally:
            _global_parsing_task = None
            if _global_parsing_owner_id == user_id:
                _global_parsing_owner_id = None
            prune_user_tasks(user_id)

        if not crashed or debug_mode or stop_event.is_set():
            return
        if not PARSE_AUTO_RESTART or PARSE_AUTO_RESTART_MAX <= 0:
            try:
                from app.notify import notify_admin

                await notify_admin(
                    f"💥 Парсинг упал (auto-restart выкл)\n"
                    f"user_id={user_id}\nsource={source}"
                )
            except Exception:
                pass
            return

        count = _auto_restart_counts.get(int(user_id), 0) + 1
        _auto_restart_counts[int(user_id)] = count
        if count > PARSE_AUTO_RESTART_MAX:
            try:
                from app.notify import notify_admin

                await notify_admin(
                    f"💥 Парсинг упал — лимит auto-restart "
                    f"({PARSE_AUTO_RESTART_MAX}) исчерпан\n"
                    f"user_id={user_id}"
                )
            except Exception:
                pass
            return

        try:
            from app.notify import notify_admin

            await notify_admin(
                f"💥 Парсинг упал — рестарт #{count}/"
                f"{PARSE_AUTO_RESTART_MAX} через "
                f"{PARSE_AUTO_RESTART_DELAY_SEC}с\n"
                f"user_id={user_id}"
            )
        except Exception:
            pass

        async def _delayed_restart():
            try:
                await asyncio.sleep(PARSE_AUTO_RESTART_DELAY_SEC)
                if stop_event.is_set():
                    return
                result = await start_parsing_for_user(
                    user_id,
                    source="auto_restart",
                    restart_if_running=True,
                    debug_mode=False,
                )
                _log.info(
                    "auto_restart user_id=%s attempt=%s result=%s",
                    user_id,
                    count,
                    result,
                )
            except Exception:
                _log.exception("auto_restart failed user_id=%s", user_id)

        asyncio.create_task(_delayed_restart(), name=f"auto_restart_{user_id}_{count}")

    async with _global_parsing_gate:
        if _global_parsing_task is not None and not _global_parsing_task.done():
            return "busy_other"
        prune_user_tasks(user_id)
        task = asyncio.create_task(_parsing_worker())
        _global_parsing_task = task
        _global_parsing_owner_id = user_id

    user_tasks.setdefault(user_id, []).append(
        {
            "name": task_name,
            "task": task,
            "stop_event": stop_event,
            "source": source,
            "debug": debug_mode,
        }
    )
    _log.info(
        "Парсинг создан user_id=%s source=%s status=%s debug=%s",
        user_id,
        source,
        status_after_stop,
        debug_mode,
    )
    try:
        from app.notify import notify_admin

        await notify_admin(
            f"▶️ Парсинг {status_after_stop}\n"
            f"user_id={user_id}\n"
            f"source={source}\n"
            f"debug={debug_mode}"
        )
    except Exception:
        _log.debug("notify_admin on start failed", exc_info=True)
    return status_after_stop


async def build_bot_status_text(user_id: int) -> str:
    """Текст статуса для кнопки «Статус»."""
    settings = await get_filter_settings_dict(user_id)
    if not settings:
        await add_filtersettings(user_id, "1", [])
        settings = await get_filter_settings_dict(user_id)

    running_self = is_parsing_running_for_user(user_id)
    global_busy = (
        _global_parsing_task is not None
        and not _global_parsing_task.done()
    )
    owner = _global_parsing_owner_id

    debug_running = any(
        t.get("debug") and t.get("task") and not t["task"].done()
        for t in user_tasks.get(user_id, [])
    )
    if running_self:
        mode = "DEBUG, без SMS" if debug_running else "обычный"
        parse_line = f"▶️ Парсинг: <b>работает</b> ({mode})"
    elif global_busy:
        parse_line = f"▶️ Парсинг: <b>занят</b> (другой user {owner})"
    else:
        parse_line = "⏹ Парсинг: <b>остановлен</b>"

    inside = is_within_window(
        settings.get("work_from"),
        settings.get("work_to"),
        settings.get("work_days"),
    )
    window_line = "сейчас внутри окна" if inside else "сейчас вне окна"

    from app.database.crud_static import get_sms_statistics
    from app.database.crud_pending_sms import count_pending_sms

    stats = await get_sms_statistics()
    today = stats.get("today") or {}
    sent_today = int(today.get("sent") or 0)
    failed_today = int(today.get("failed") or 0)
    if SMS_DAILY_LIMIT > 0:
        limit_line = f"SMS сегодня: {sent_today}/{SMS_DAILY_LIMIT} (ошибок {failed_today})"
    else:
        limit_line = f"SMS сегодня: {sent_today} ок / {failed_today} ошибок (лимит выкл.)"

    pending_count = await count_pending_sms()
    pending_line = f"⏳ Очередь SMS (ожидают окна): <b>{pending_count}</b>"

    now = now_in_bot_tz().strftime("%H:%M")
    return (
        f"📊 <b>Статус бота</b> ({now})\n\n"
        f"{parse_line}\n"
        f"{schedule_summary(settings)}\n"
        f"Окно: {window_line}\n\n"
        f"{limit_line}\n"
        f"{pending_line}"
    )


async def _launch_parsing_from_menu(
    call: CallbackQuery,
    *,
    debug_mode: bool,
    source: str,
) -> None:
    """Общий запуск обычного / DEBUG парсинга из меню."""
    user_id = int(call.from_user.id)
    mode_label = "DEBUG" if debug_mode else "обычный"
    _log.info("Кнопка запуска (%s) нажата, user_id=%s", mode_label, user_id)

    if is_parsing_running_for_user(user_id):
        await call.message.answer("⚠️ Предыдущая задача парсинга останавливается...")

    status = await start_parsing_for_user(
        user_id,
        source=source,
        restart_if_running=True,
        debug_mode=debug_mode,
    )

    if status == "stop_timeout":
        await call.message.answer(
            "Не удалось дождаться остановки предыдущего парсинга. Попробуйте через минуту."
        )
        return
    if status == "busy_other":
        await call.message.answer(
            "⚠️ Уже выполняется парсинг (на сервере одновременно работает только один Chrome). "
            "Дождитесь окончания или остановите задачу у пользователя, который её запустил."
        )
        return
    if status == "no_filter":
        await call.message.answer(
            "Настройки фильтра не найдены. Откройте «Настройки» → «Фильтр объявлений» "
            "или нажмите /start."
        )
        return

    filter_settings = await get_filter_settings_dict(user_id)
    typeavto = filter_settings.get("typeavto") or "1"
    region_str = _region_str_from_settings(filter_settings.get("region_id") or 0)
    year_first = filter_settings.get("year_first") or "0"
    year_second = filter_settings.get("year_second") or "0"
    price_first = filter_settings.get("price_first") or "0"
    price_second = filter_settings.get("price_second") or "0"
    schedule_line = schedule_summary(filter_settings)

    if debug_mode:
        head = (
            "<b>🧪 DEBUG-парсинг запущен</b>\n"
            "Поиск как обычно, но SMS не шлём и <b>номера/объявления в БД не пишем</b> "
            "(чтобы потом обычный Запуск мог слать SMS).\n\n"
            "<b>Актуальные настройки:</b>"
        )
        tail = "DEBUG работает в фоне. Стоп — кнопка «Стоп»."
    else:
        head = "<b>Парсинг запущен с настройками:</b>"
        tail = "Парсинг запущен в фоне. Вы можете продолжать пользоваться ботом."

    await call.message.answer(
        f"{head}\n\n"
        f"Тип авто: {type_mapping.get(str(typeavto), typeavto)}\n"
        f"Регионы: {region_str}\n"
        f"Годы: {year_first} - {year_second}\n"
        f"Цена: {price_first} - {price_second}\n\n"
        f"<i>{schedule_line}</i>"
    )
    await call.message.answer(tail, reply_markup=start_kb())


# === Старт парсинга ===
@router.callback_query(F.data == "startparsing")
async def startparsing(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    await call.answer("Запуск парсинга", show_alert=False)
    await _launch_parsing_from_menu(call, debug_mode=False, source="manual")


@router.callback_query(F.data == "debugparsing")
async def debugparsing(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    await call.answer("Запуск DEBUG", show_alert=False)
    await _launch_parsing_from_menu(call, debug_mode=True, source="debug")


@router.callback_query(F.data == "stopparsing")
async def stopparsing(call: CallbackQuery):
    await call.answer()
    user_id = int(call.from_user.id)
    stopped = await stop_parsing_for_user(user_id, source="manual_menu")
    if stopped:
        # Даём потоку чуть времени завершиться после kill Chrome
        t = _global_parsing_task
        if t is not None and not t.done():
            try:
                await asyncio.wait_for(asyncio.shield(t), timeout=15.0)
            except asyncio.TimeoutError:
                _log.warning("Парсинг ещё завершается после стопа user_id=%s", user_id)
        prune_user_tasks(user_id)
        await call.message.answer(
            "⏹ Парсинг остановлен.",
            reply_markup=start_kb(),
        )
    else:
        prune_user_tasks(user_id)
        if (
            _global_parsing_task is not None
            and not _global_parsing_task.done()
            and _global_parsing_owner_id
            and _global_parsing_owner_id != user_id
        ):
            await call.message.answer(
                f"⚠️ Сейчас парсинг запущен другим пользователем (id {_global_parsing_owner_id}). "
                "Остановить можно только из его аккаунта.",
                reply_markup=start_kb(),
            )
        else:
            await call.message.answer(
                "Сейчас нет вашего активного парсинга.",
                reply_markup=start_kb(),
            )


@router.callback_query(F.data == "bot_status")
async def bot_status_handler(call: CallbackQuery):
    await call.answer()
    text = await build_bot_status_text(int(call.from_user.id))
    await call.message.answer(text, reply_markup=start_kb())


@router.callback_query(F.data == "mytask")
async def my_tasks_handler(call: CallbackQuery, state: FSMContext):
    await call.answer()
    user_id = int(call.from_user.id)
    active = prune_user_tasks(user_id)

    back_kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [InlineKeyboardButton(text="⬅️ В меню", callback_data="backstart")],
        ]
    )

    if not active:
        await call.message.answer(
            "📋 Активных задач нет.",
            reply_markup=back_kb,
        )
        return

    msg_lines = ["<b>Ваши активные задачи:</b>"]
    keyboard_buttons = []
    for idx, task_dict in enumerate(active, start=1):
        task_name = task_dict.get("name") or "Задача"
        src = task_dict.get("source") or ""
        src_note = f" ({src})" if src and src not in ("manual", "debug") else ""
        msg_lines.append(f"{idx}. {task_name} — ▶️ в процессе{src_note}")
        keyboard_buttons.append(
            [
                InlineKeyboardButton(
                    text=f"⏹ Остановить: {task_name}",
                    callback_data=f"stop_task_{idx}",
                )
            ]
        )
    keyboard_buttons.append(
        [InlineKeyboardButton(text="⬅️ В меню", callback_data="backstart")]
    )

    await call.message.answer(
        "\n".join(msg_lines),
        reply_markup=InlineKeyboardMarkup(inline_keyboard=keyboard_buttons),
    )


@router.callback_query(F.data.startswith("stop_task_"))
async def stop_task_handler(call: CallbackQuery):
    await call.answer()
    try:
        task_num = int(call.data.split("_")[-1])
    except ValueError:
        await call.message.answer("Неверный номер задачи.")
        return

    user_id = int(call.from_user.id)
    active = prune_user_tasks(user_id)

    if not (0 < task_num <= len(active)):
        await call.message.answer(
            "Задача не найдена (возможно, уже завершена).",
            reply_markup=start_kb(),
        )
        return

    task_dict = active[task_num - 1]
    task_name = task_dict.get("name") or f"#{task_num}"
    if _is_parsing_task_name(task_dict.get("name")):
        await stop_parsing_for_user(user_id, source="my_tasks")
        t = task_dict.get("task")
        if t is not None and not t.done():
            try:
                await asyncio.wait_for(asyncio.shield(t), timeout=15.0)
            except asyncio.TimeoutError:
                pass
    else:
        stop_event = task_dict.get("stop_event")
        if stop_event:
            stop_event.set()

    prune_user_tasks(user_id)
    await call.message.answer(
        f"⏹ Задача «{task_name}» остановлена.",
        reply_markup=start_kb(),
    )


# === Единые настройки (хаб) ===
@router.callback_query(F.data == "settings_hub")
@router.callback_query(F.data == "settingsparsing")
async def settings_hub_handler(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    await state.clear()
    user_id = int(call.from_user.id)
    settings = await get_filter_settings_dict(user_id)
    if not settings:
        await add_filtersettings(user_id, "1", [])
        settings = await get_filter_settings_dict(user_id)

    text = (
        "<b>Настройки бота</b>\n\n"
        "Здесь можно выбрать, когда бот работает сам,\n"
        "какие объявления брать и как слать SMS.\n"
        "Debug-парсинг — пробный прогон без SMS и без записи в БД.\n\n"
        f"{schedule_summary(settings)}"
    )
    await call.message.answer(text, reply_markup=settings_hub_kb())


@router.callback_query(F.data == "settings_general")
async def settings_general_handler(call: CallbackQuery):
    await safe_delete_message(call.message)
    text = (
        "<b>Общие параметры</b>\n"
        "<i>Меняются в файле .env на сервере</i>\n\n"
        f"📄 <b>Страницы поиска:</b> {PARSING_SEARCH_PAGES}\n"
        "Сколько «листает» выдачу Auto.RIA за раз "
        "<i>(на каждый выбранный регион)</i>\n\n"
        f"🆕 <b>Свежесть объявлений:</b> {PARSING_REPUBLISHED_LAST if PARSING_REPUBLISHED_LAST else 'без фильтра'}\n"
        "Насколько свежие объявления брать (дней; 0 — все)\n\n"
        f"🏛 <b>Skip newauto:</b> {'да' if PARSING_SKIP_NEWAUTO else 'нет'}\n"
        "Пропускать салоны (/newauto/) без кнопки телефона\n\n"
        f"🚫 <b>Пропуск уже отправленных:</b> "
        f"{'да (true)' if SMS_SKIP_ALREADY_SENT else 'нет (false)'}\n"
        "Если на номер уже была SMS в БД — объявление пропускаем\n"
        f"<i>.env: SMS_SKIP_ALREADY_SENT</i>\n\n"
        f"🔁 <b>Cooldownoldown повторной SMS:</b> "
        f"{'не используется (skip=true)' if SMS_SKIP_ALREADY_SENT else ('запрещён' if SMS_RESEND_AFTER_DAYS <= 0 else f'{SMS_RESEND_AFTER_DAYS} дн.')}\n"
        "Имеет смысл только при SMS_SKIP_ALREADY_SENT=false\n\n"
        f"🚦 <b>Лимит SMS в день:</b> "
        f"{'выкл (0)' if SMS_DAILY_LIMIT <= 0 else SMS_DAILY_LIMIT}\n"
        "Сколько успешных SMS максимум за сутки\n\n"
        f"🕰 <b>Дневной отчёт:</b> "
        f"{'выкл' if DAILY_REPORT_HOUR < 0 else f'в {DAILY_REPORT_HOUR:02d}:00'}\n"
        "Автосводка в Telegram\n\n"
        f"🌐 <b>Рестарт Chrome:</b> "
        f"{'выкл' if CHROME_RESTART_EVERY_ADS <= 0 else f'каждые {CHROME_RESTART_EVERY_ADS} объявл.'}\n"
        "Как часто перезапускать браузер для стабильности\n\n"
        f"😴 <b>Пауза между объявлениями:</b> "
        f"{PARSING_PAUSE_BETWEEN_ADS_SEC_MIN}–{PARSING_PAUSE_BETWEEN_ADS_SEC_MAX} с\n"
        f"🌙 <b>Пауза между проходами:</b> "
        f"{PARSING_PAUSE_BETWEEN_ITERATIONS_SEC_MIN}–{PARSING_PAUSE_BETWEEN_ITERATIONS_SEC_MAX} с"
    )
    kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [InlineKeyboardButton(text="⬅️ К настройкам", callback_data="settings_hub")]
        ]
    )
    await call.message.answer(text, reply_markup=kb)


@router.callback_query(F.data == "settings_schedule")
async def settings_schedule_handler(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    await state.clear()
    user_id = int(call.from_user.id)
    settings = await get_filter_settings_dict(user_id)
    if not settings:
        await add_filtersettings(user_id, "1", [])
        settings = await get_filter_settings_dict(user_id)

    text = (
        "<b>Расписание работы</b>\n\n"
        "<b>Вручную</b> — бот работает только когда нажали «Запуск».\n"
        "<b>По расписанию</b> — в выбранные дни и часы бот стартует сам; "
        "в конце окна останавливается. Кнопку «Запуск» можно нажать в любое время.\n\n"
        f"{schedule_summary(settings)}"
    )
    await call.message.answer(text, reply_markup=schedule_kb(settings.get("run_mode")))


@router.callback_query(F.data == "schedule_toggle_mode")
async def schedule_toggle_mode(call: CallbackQuery):
    user_id = int(call.from_user.id)
    settings = await get_filter_settings_dict(user_id)
    current = settings.get("run_mode") or RUN_MODE_MANUAL
    new_mode = RUN_MODE_MANUAL if current == RUN_MODE_SCHEDULE else RUN_MODE_SCHEDULE
    await update_run_mode(user_id, new_mode)
    settings = await get_filter_settings_dict(user_id)
    await call.answer(
        "Включён режим «По расписанию»" if new_mode == RUN_MODE_SCHEDULE else "Режим «Вручную»",
        show_alert=False,
    )
    text = (
        "<b>Расписание работы</b>\n\n"
        f"{schedule_summary(settings)}\n\n"
        + (
            "Бот будет сам запускаться и останавливаться по окну и дням."
            if new_mode == RUN_MODE_SCHEDULE
            else "Автозапуск выключен. Используйте кнопку «Запуск»."
        )
    )
    await call.message.edit_text(text, reply_markup=schedule_kb(settings.get("run_mode")))


@router.callback_query(F.data == "schedule_set_from")
async def schedule_set_from(call: CallbackQuery, state: FSMContext):
    await state.set_state(ScheduleState.waiting_from)
    await call.message.answer(
        "Введите время <b>начала</b> работы в формате <code>ЧЧ:ММ</code>\n"
        "Например: <code>09:00</code>\n\n"
        "Окно должно быть в пределах одного дня (до полуночи)."
    )
    await call.answer()


@router.callback_query(F.data == "schedule_set_to")
async def schedule_set_to(call: CallbackQuery, state: FSMContext):
    await state.set_state(ScheduleState.waiting_to)
    await call.message.answer(
        "Введите время <b>окончания</b> работы в формате <code>ЧЧ:ММ</code>\n"
        "Например: <code>20:00</code>"
    )
    await call.answer()


@router.message(ScheduleState.waiting_from)
async def schedule_from_message(message: Message, state: FSMContext):
    user_id = int(message.from_user.id)
    settings = await get_filter_settings_dict(user_id)
    raw = (message.text or "").strip()
    ok, err = validate_work_window(raw, settings.get("work_to") or DEFAULT_WORK_TO)
    if not ok:
        await message.answer(f"⚠️ {err}\nПопробуйте ещё раз или /start для выхода.")
        return
    await update_work_from(user_id, format_hhmm(raw))
    await state.clear()
    settings = await get_filter_settings_dict(user_id)
    await message.answer(
        f"✅ Время «с» сохранено.\n\n{schedule_summary(settings)}",
        reply_markup=schedule_kb(settings.get("run_mode")),
    )


@router.message(ScheduleState.waiting_to)
async def schedule_to_message(message: Message, state: FSMContext):
    user_id = int(message.from_user.id)
    settings = await get_filter_settings_dict(user_id)
    raw = (message.text or "").strip()
    ok, err = validate_work_window(settings.get("work_from") or DEFAULT_WORK_FROM, raw)
    if not ok:
        await message.answer(f"⚠️ {err}\nПопробуйте ещё раз или /start для выхода.")
        return
    await update_work_to(user_id, format_hhmm(raw))
    await state.clear()
    settings = await get_filter_settings_dict(user_id)
    await message.answer(
        f"✅ Время «до» сохранено.\n\n{schedule_summary(settings)}",
        reply_markup=schedule_kb(settings.get("run_mode")),
    )


@router.callback_query(F.data == "schedule_days")
async def schedule_days_menu(call: CallbackQuery, state: FSMContext):
    await state.clear()
    settings = await get_filter_settings_dict(int(call.from_user.id))
    days = settings.get("work_days")
    await call.message.answer(
        "<b>Дни недели</b>\n\n"
        "Нажмите на день, чтобы включить или выключить.\n"
        f"Сейчас: <b>{format_work_days(days)}</b>",
        reply_markup=schedule_days_kb(days),
    )
    await call.answer()


@router.callback_query(lambda c: c.data and c.data.startswith("toggle_weekday_"))
async def toggle_weekday_handler(call: CallbackQuery):
    user_id = int(call.from_user.id)
    day = int(call.data.split("_")[-1])
    settings = await get_filter_settings_dict(user_id)
    days = set(normalize_work_days(settings.get("work_days")))
    if day in days:
        if len(days) == 1:
            await call.answer("Нужен хотя бы один день", show_alert=True)
            return
        days.remove(day)
    else:
        days.add(day)
    await update_work_days(user_id, sorted(days))
    settings = await get_filter_settings_dict(user_id)
    try:
        await call.message.edit_text(
            "<b>Дни недели</b>\n\n"
            "Нажмите на день, чтобы включить или выключить.\n"
            f"Сейчас: <b>{format_work_days(settings.get('work_days'))}</b>",
            reply_markup=schedule_days_kb(settings.get("work_days")),
        )
    except Exception:
        pass
    await call.answer()


@router.callback_query(F.data == "schedule_days_weekdays")
async def schedule_days_weekdays(call: CallbackQuery):
    user_id = int(call.from_user.id)
    await update_work_days(user_id, [0, 1, 2, 3, 4])
    settings = await get_filter_settings_dict(user_id)
    await call.message.edit_text(
        "<b>Дни недели</b>\n\nСейчас: <b>Пн–Пт</b>",
        reply_markup=schedule_days_kb(settings.get("work_days")),
    )
    await call.answer("Пн–Пт")


@router.callback_query(F.data == "schedule_days_all")
async def schedule_days_all(call: CallbackQuery):
    user_id = int(call.from_user.id)
    await update_work_days(user_id, [0, 1, 2, 3, 4, 5, 6])
    settings = await get_filter_settings_dict(user_id)
    await call.message.edit_text(
        "<b>Дни недели</b>\n\nСейчас: <b>ежедневно</b>",
        reply_markup=schedule_days_kb(settings.get("work_days")),
    )
    await call.answer("Все дни")


# Фильтр объявлений (бывш. «Настройка»)
@router.callback_query(F.data == "backsettings")
@router.callback_query(F.data == "settings_filter")
async def settings_filter_handler(call: CallbackQuery):
    await safe_delete_message(call.message)

    settings = await get_filter_settings_dict(int(call.from_user.id))

    if settings:
        saved_type = settings.get("typeavto", "Не указано")
        type_name = type_mapping.get(saved_type, saved_type)

        saved_regions = settings.get("region_id")
        if saved_regions:
            if isinstance(saved_regions, list):
                region_names = [name for name, reg_id in regions.items() if reg_id in saved_regions]
            else:
                region_names = [name for name, reg_id in regions.items() if reg_id == saved_regions]
        else:
            region_names = ["Все регионы"]

        year_first = settings.get("year_first") or "Не указано"
        year_second = settings.get("year_second") or "Не указано"
        price_first = settings.get("price_first") or "Не указано"
        price_second = settings.get("price_second") or "Не указано"

        result_text = (
            f"📌 <b>Фильтр объявлений</b>\n"
            f"<i>Какие объявления брать с Auto.RIA</i>\n\n"
            f"🚗 <b>Тип авто:</b> {type_name}\n"
            f"🏠 <b>Регионы:</b> {', '.join(region_names)}\n"
            f"🪙 <b>Год:</b> {year_first} - {year_second}\n"
            f"💸 <b>Цена:</b> {price_first} - {price_second}\n\n"
            f"<b>Что изменить?</b>"
        )
    else:
        result_text = "<b>Фильтр не найден.</b>\nСделайте выбор:"

    await call.message.answer(result_text, reply_markup=settings_kb())


@router.callback_query(F.data == "reset_filters")
async def reset_filters_handler(call: CallbackQuery):
    user_id = int(call.from_user.id)
    ok = await reset_filter_settings(user_id)
    if not ok:
        await add_filtersettings(user_id, "1", [])
        await reset_filter_settings(user_id)
    await call.answer("Фильтры сброшены", show_alert=False)
    await settings_filter_handler(call)


# === Тип авто === 
@router.callback_query(F.data == "typeAuto")
async def typeAutosett(call:CallbackQuery):
    await safe_delete_message(call.message)
    
    list_type = {
        "Легковые": "legtype",
        "Мото": "mototype",
        "Грузовики": "vantype",
        "Прицепы": "priceptype",
        "Спецтехника": "spectype",
        "Автобусы": "avtobtype",
        "Водный транспорт": "vodnitype",
        "Воздушный транспорт": "vozdtype",
        "Автодома": "avtodobtype"
    }
    
    
    keyboard_buttons = []
    row = []
    # Создаем две кнопки в ряд
    for i, (label, callback) in enumerate(list_type.items(), start=1):
        row.append(InlineKeyboardButton(text=f"🔻{label}", callback_data=callback))
        if i % 2 == 0:
            keyboard_buttons.append(row)
            row = []
            
    # Если осталась неполная строка, добавляем её
    if row:
        keyboard_buttons.append(row)
    keyboard_buttons.append([InlineKeyboardButton(text="⬅️", callback_data="backsettings")])

    kb = InlineKeyboardMarkup(inline_keyboard=keyboard_buttons)
    
    await call.message.answer(
        f"Сделайте выбор:",
        reply_markup=kb
    )
    

# Обработчик для выбора типа автомобиля
@router.callback_query(F.data.in_(["legtype", "mototype", "vantype", "priceptype", "spectype", "avtobtype", "vodnitype", "vozdtype", "avtodobtype"]))
async def handle_type_selection(call: CallbackQuery):
    await safe_delete_message(call.message)
    
    selected_type = call.data
    
    # Словарь соответствия callback_data и ID типа транспорта:
    type_mapping = {
        "legtype": 1,
        "mototype": 2,
        "vantype": 6,
        "priceptype": 5,
        "spectype": 4,
        "avtobtype": 7,
        "vodnitype": 3,
        "vozdtype": 9,
        "avtodobtype": 8
    }
    
    type_id = type_mapping.get(selected_type, "Неизвестно")
    
    await call.answer(f"Вы обнвоили тип транспорта ✅", show_alert=True)
    
    await update_typeavto(int(call.from_user.id), str(type_id))
    await asyncio.sleep(.4)
    
    await call.message.answer(f"<b>Вас вернуло в меню настроек\n\nСделайте выбор:</b>", reply_markup=settings_kb())


# === Год ===
@router.callback_query(F.data == "yearsettings") 
async def yearsettings(call:CallbackQuery, state:FSMContext):
    await safe_delete_message(call.message)
    
    kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(text=f"⬆️Год от", callback_data=f"yearfirst"),
                InlineKeyboardButton(text=f"⏺️Год до", callback_data=f"yearsecond")
            ],
            [
                InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
            ]
        ]
    )
    
    await call.message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)
    

@router.callback_query(F.data.in_(["yearfirst", "yearsecond"]))
async def getYearSettings(call:CallbackQuery, state:FSMContext):
    await safe_delete_message(call.message)
    
    kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettyear")]
        ]
    )
    
    if call.data == "yearfirst":
        await state.clear()
        await call.message.answer(f"<b>Введите год от которого начинать поиск:</b>", reply_markup=kb)
        await state.set_state(settingsState.yearFirst)
    elif call.data == "yearsecond":
        await state.clear()
        await call.message.answer(f"<b>Введите год до которого совершать поиск:</b>", reply_markup=kb)
        await state.set_state(settingsState.yearSecond)
    else:
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    InlineKeyboardButton(text=f"⬆️Год от", callback_data=f"yearfirst"),
                    InlineKeyboardButton(text=f"⏺️Год до", callback_data=f"yearsecond")
                ],
                [
                    InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
                ]
            ]
        )
        
        await call.message.answer(f"⚠️ Вы ничего не выбрали", reply_markup=kb)
    

@router.message(settingsState.yearFirst)
async def firstYearGet(message: Message, state:FSMContext):
    try:
        year = int(message.text)
        
        await update_year_first(int(message.from_user.id), int(year))
        await message.answer(f"Год {year} сохранен ✅")
        
        await asyncio.sleep(.4)
        
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    InlineKeyboardButton(text=f"⬆️Год от", callback_data=f"yearfirst"),
                    InlineKeyboardButton(text=f"⏺️Год до", callback_data=f"yearsecond")
                ],
                [
                    InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
                ]
            ]
        )
        
        await message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)
        await state.clear()
    except:
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettyear")]
            ]
        )
        
        await message.answer(f"Вы ввели не верно число (2012, 2004...)\n\nВведите корректное число:", reply_markup=kb)
        await state.set_state(settingsState.yearFirst)


@router.message(settingsState.yearSecond)
async def secondYearGet(message: Message, state:FSMContext):
    try:
        year = int(message.text)
        
        await update_year_second(int(message.from_user.id), int(year))
        await message.answer(f"Год {year} сохранен ✅")
        
        await asyncio.sleep(.4)
        
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    InlineKeyboardButton(text=f"⬆️Год от", callback_data=f"yearfirst"),
                    InlineKeyboardButton(text=f"⏺️Год до", callback_data=f"yearsecond")
                ],
                [
                    InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
                ]
            ]
        )
        
        await message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)
        await state.clear()
    except:
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettyear")]
            ]
        )
        
        await message.answer(f"Вы ввели не верно число (2012, 2004...)\n\nВведите корректное число:", reply_markup=kb)
        await state.set_state(settingsState.yearSecond)

# Кнопка назад в вводе года
@router.callback_query(F.data == "backsettyear", settingsState.yearFirst)
@router.callback_query(F.data == "backsettyear", settingsState.yearSecond)
async def backsettyear(call:CallbackQuery, state:FSMContext):
    await safe_delete_message(call.message)
    kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(text=f"⬆️Год от", callback_data=f"yearfirst"),
                InlineKeyboardButton(text=f"⏺️Год до", callback_data=f"yearsecond")
            ],
            [
                InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
            ]
        ]
    )
    
    await call.message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)
    await state.clear()


# === Настройка цены ===
@router.callback_query(F.data == "pricesettings")
async def pricesettings(call:CallbackQuery):
    await safe_delete_message(call.message)
    
    kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(text=f"⬆️Цена от", callback_data=f"pricefirst"),
                InlineKeyboardButton(text=f"⏺️Цена до", callback_data=f"pricesecond")
            ],
            [
                InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
            ]
        ]
    )
    
    await call.message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)


@router.callback_query(F.data.in_(["pricefirst", "pricesecond"]))
async def btnPriceSett(call:CallbackQuery, state:FSMContext):
    await safe_delete_message(call.message)
    
    kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettprice")]
        ]
    )
    
    if call.data == "pricefirst":
        await state.clear()
        await call.message.answer(f"<b>Введите цену от которой начинать поиск:</b>", reply_markup=kb)
        await state.set_state(settingsState.priceFirst)
    elif call.data == "pricesecond":
        await state.clear()
        await call.message.answer(f"<b>Введите цену до которой совершать поиск:</b>", reply_markup=kb)
        await state.set_state(settingsState.priceSecond)
    else:
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    InlineKeyboardButton(text=f"⬆️Цена от", callback_data=f"pricefirst"),
                    InlineKeyboardButton(text=f"⏺️Цена до", callback_data=f"pricesecond")
                ],
                [
                    InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
                ]
            ]
        )
        
        await call.message.answer(f"⚠️ Вы ничего не выбрали", reply_markup=kb)
    
    
@router.message(settingsState.priceFirst)
async def firstPriceGet(message: Message, state:FSMContext):
    try:
        price = float(message.text)
        
        await update_price_first(int(message.from_user.id), price)
        await message.answer(f"Цена {price} сохранена ✅")
        
        await asyncio.sleep(.4)
        
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    InlineKeyboardButton(text=f"⬆️Цена от", callback_data=f"pricefirst"),
                    InlineKeyboardButton(text=f"⏺️Цена до", callback_data=f"pricesecond")
                ],
                [
                    InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
                ]
            ]
        )
        
        await message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)
        await state.clear()
    except:
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettprice")]
            ]
        )
        
        await message.answer(f"Вы ввели не верно число (1000.2, 10000, 23500...)\n\nВведите корректное число:", reply_markup=kb)
        await state.set_state(settingsState.priceFirst)


@router.message(settingsState.priceSecond)
async def secondPriceGet(message: Message, state:FSMContext):
    try:
        price = float(message.text)
        
        await update_price_second(int(message.from_user.id), price)
        await message.answer(f"Цена {price} сохранена ✅")
        
        await asyncio.sleep(.4)
        
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [
                    InlineKeyboardButton(text=f"⬆️Цена от", callback_data=f"pricefirst"),
                    InlineKeyboardButton(text=f"⏺️Цена до", callback_data=f"pricesecond")
                ],
                [
                    InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
                ]
            ]
        )
        
        await message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)
        await state.clear()
    except:
        kb = InlineKeyboardMarkup(
            inline_keyboard=[
                [InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettprice")]
            ]
        )
        
        await message.answer(f"Вы ввели не верно число (1000.2, 10000, 23500...)\n\nВведите корректное число:", reply_markup=kb)
        await state.set_state(settingsState.priceSecond)



@router.callback_query(F.data == "backsettprice", settingsState.priceFirst)
@router.callback_query(F.data == "backsettprice", settingsState.priceSecond)
async def backsettprice(call:CallbackQuery, state:FSMContext):
    await safe_delete_message(call.message)
    kb = InlineKeyboardMarkup(
        inline_keyboard=[
            [
                InlineKeyboardButton(text=f"⬆️Цена от", callback_data=f"pricefirst"),
                InlineKeyboardButton(text=f"⏺️Цена до", callback_data=f"pricesecond")
            ],
            [
                InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")
            ]
        ]
    )
    
    await call.message.answer(f"<b>Сделайте свой выбор:</b>", reply_markup=kb)
    await state.clear()


# === Регион ===
def build_regions_keyboard(selected_regions: list) -> InlineKeyboardMarkup:
    keyboard = []  # Список рядов кнопок
    row = []
    for i, (region_name, region_id) in enumerate(regions.items(), start=1):
        # Если регион выбран, добавляем галочку в названии кнопки
        button_text = f"✅ {region_name}" if region_id in selected_regions else region_name
        row.append(InlineKeyboardButton(text=button_text, callback_data=f"toggle_region_{region_id}"))
        if i % 2 == 0:
            keyboard.append(row)
            row = []
    if row:
        keyboard.append(row)
    # Добавляем кнопку подтверждения в отдельном ряду
    keyboard.append([InlineKeyboardButton(text="Подтвердить выбор", callback_data="confirm_regions")])
    keyboard.append([InlineKeyboardButton(text=f"⬅️", callback_data=f"backsettings")])
    return InlineKeyboardMarkup(inline_keyboard=keyboard)


# Обработчик для запуска выбора регионов
@router.callback_query(F.data == "regionsettings")
async def regionsettings(call: CallbackQuery, state: FSMContext):
    # Инициализируем список выбранных регионов, если его еще нет
    data = await state.get_data()
    selected_regions = data.get("selected_regions", [])
    await state.update_data(selected_regions=selected_regions)
    kb = build_regions_keyboard(selected_regions)
    await call.message.answer("Выберите регионы (нажмите на регион для переключения):", reply_markup=kb)
    await call.answer()


# Обработчик переключения (toggle) выбора региона
@router.callback_query(lambda c: c.data.startswith("toggle_region_"))
async def toggle_region_handler(call: CallbackQuery, state: FSMContext):
    region_id = int(call.data.split("_")[-1])
    data = await state.get_data()
    selected_regions = data.get("selected_regions", [])
    if region_id in selected_regions:
        selected_regions.remove(region_id)
    else:
        selected_regions.append(region_id)
    await state.update_data(selected_regions=selected_regions)
    # Обновляем клавиатуру с новым состоянием
    kb = build_regions_keyboard(selected_regions)
    await call.message.edit_reply_markup(reply_markup=kb)
    await call.answer()


# Обработчик подтверждения выбора регионов
@router.callback_query(F.data == "confirm_regions")
async def confirm_regions_handler(call: CallbackQuery, state: FSMContext):
    data = await state.get_data()
    selected_regions = data.get("selected_regions", [])
    
    await update_region_id(int(call.from_user.id), selected_regions)
    
    await call.answer(f"Ваши регионы сохранены ✅", show_alert=True)

    await safe_delete_message(call.message)
    await call.message.answer(f"<b>Сделайте выбор:</b>", reply_markup=settings_kb())


@router.callback_query(F.data == "backstart")
async def backstart(call:CallbackQuery, state:FSMContext):
    await safe_delete_message(call.message)
    await call.message.answer(f"<b>Добро пожаловать в бота AutoRia</b>", reply_markup=start_kb())
    await state.clear()


@router.callback_query(F.data == "stats_hub")
async def stats_hub_handler(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    await state.clear()
    await call.message.answer(
        "<b>📈 Статистика</b>\n\nВыберите раздел:",
        reply_markup=stats_hub_kb(),
    )


# ---------------------------------------------
# ----------- Получение данных ----------------
async def get_autoinfo_unique_dates(for_selection: bool = False, selected_dates: list = None) -> tuple[InlineKeyboardMarkup, str]:
    if selected_dates is None:
        selected_dates = []
    
    selected_dates_set = set(selected_dates)

    # Только объявления с успешно отправленной SMS
    entries = await list_autoinfo_with_successful_sms()

    # Получаем уникальные даты в формате "дд.мм.гггг"
    unique_dates = set()
    for entry in entries:
        date_str = entry.created_at.strftime("%d.%m.%Y")
        unique_dates.add(date_str)

    # Сортируем даты по возрастанию
    sorted_dates = sorted(unique_dates, key=lambda d: datetime.strptime(d, "%d.%m.%Y"))

    if sorted_dates:
        if for_selection:
            # Режим выбора дат для удаления
            buttons = []
            for date in sorted_dates:
                # Отмечаем выбранные даты
                text = f"✅ {date}" if date in selected_dates_set else date
                buttons.append([InlineKeyboardButton(text=text, callback_data=f"select_stat_{date}")])
            
            # Добавляем кнопки управления
            buttons.append([InlineKeyboardButton(text="🗑 Удалить выбранные", callback_data="confirm_delete_stats")])
            buttons.append([InlineKeyboardButton(text="⬅️ Отмена", callback_data="statistic")])
            
            keyboard = InlineKeyboardMarkup(inline_keyboard=buttons)
            text = "👇 Выберите даты для удаления:"
        else:
            # Обычный режим
            keyboard = InlineKeyboardMarkup(
                inline_keyboard=[
                    [InlineKeyboardButton(text=date, callback_data=f"stat_{date}")]
                    for date in sorted_dates
                ] + [
                    [InlineKeyboardButton(text="🗑 Очистить статистику", callback_data="clear_stats_menu")],
                    [InlineKeyboardButton(text="⬅️ К статистике", callback_data="stats_hub")]
                ]
            )
            text = "🟢 Выбери дату (только с успешной SMS):"
    else:
        keyboard = InlineKeyboardMarkup(
            inline_keyboard=[
                [InlineKeyboardButton(text="⬅️ К статистике", callback_data="stats_hub")]
            ]
        )
        
        text = "🔴 Данных нет"

    return keyboard, text


@router.callback_query(F.data == "statistic")
async def statistic(call: CallbackQuery):
    await safe_delete_message(call.message)

    kb, text = await get_autoinfo_unique_dates()
    await call.message.answer(f"<b>{text}</b>", reply_markup=kb)



@router.callback_query(F.data.startswith("stat_"))
async def handle_stat_by_date(call: CallbackQuery):
    await safe_delete_message(call.message)

    # Извлекаем дату
    date_str = call.data.split("stat_")[1]
    try:
        target_date = datetime.strptime(date_str, "%d.%m.%Y").date()
    except ValueError:
        await call.message.answer("Неверный формат даты.")
        return

    start_dt = datetime.combine(target_date, datetime.min.time())
    end_dt = datetime.combine(target_date, datetime.max.time())

    entries = await list_autoinfo_with_successful_sms(start_dt=start_dt, end_dt=end_dt)

    if not entries:
        await call.message.answer(f"Нет записей с успешной SMS за {date_str}")
        return

    # Генерация текста
    lines = [f"Авто с успешной SMS за {date_str}:\n"]
    for item in entries:
        year = item.year or ""
        phones = item.all_phones if isinstance(item.all_phones, list) else []
        phones_s = ", ".join(str(p) for p in phones if p) or "—"
        lines.append(
            f"{item.name} {year}\n"
            f"{item.link}\n"
            f"{phones_s}\n"
        )

    text_output = "\n".join(lines)

    # /tmp — /app в Docker недоступен для записи (процесс uid 1000)
    filename = os.path.join(
        tempfile.gettempdir(), f"autos_{date_str.replace('.', '-')}.txt"
    )
    with open(filename, "w", encoding="utf-8") as f:
        f.write(text_output)

    try:
        file = FSInputFile(filename)
        await call.message.answer_document(file, caption=f"Авто за {date_str}")
    finally:
        try:
            os.remove(filename)
        except OSError:
            pass

    # Кнопка назад
    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="➖ Удалить данные за эту дату", callback_data=f"deldany_{date_str}")],
        [InlineKeyboardButton(text="⬅️ Назад", callback_data="statistic")]
    ])
    
    await call.message.answer("Выбери дейстивя:", reply_markup=kb)



@router.callback_query(F.data.startswith("deldany_"))
async def delete_data_by_date(call: CallbackQuery):
    await safe_delete_message(call.message)

    date_str = call.data.split("deldany_")[1]
    try:
        target_date = datetime.strptime(date_str, "%d.%m.%Y").date()
    except ValueError:
        await call.message.answer("Неверный формат даты для удаления.")
        return

    start_dt = datetime.combine(target_date, datetime.min.time())
    end_dt = datetime.combine(target_date, datetime.max.time())

    async with async_session() as session:
        async with session.begin():
            result = await session.execute(
                select(AutoInfo).where(
                    AutoInfo.created_at >= start_dt,
                    AutoInfo.created_at <= end_dt
                )
            )
            entries = result.scalars().all()
            count = len(entries)

            for entry in entries:
                await session.delete(entry)

    await call.message.answer(f"Удалено {count} записей за {date_str}.")

    # Кнопка назад к списку дат
    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="⬅️ Назад", callback_data="statistic")]
    ])
    
    await call.message.answer("Выбери другую дату:", reply_markup=kb)



@router.callback_query(F.data == "staticsms")
async def staticsms(call:CallbackQuery):
    await safe_delete_message(call.message)
    
    # Используем новую детальную статистику
    try:
        from app.database.crud_static import get_sms_statistics
        from app.database.crud_pending_sms import count_pending_sms

        stats = await get_sms_statistics()
        pending = await count_pending_sms()
        
        message = f"""📊 <b>Детальная SMS статистика</b>

🗓 <b>Сегодня:</b>
✅ Отправлено: {stats['today']['sent']}
❌ Неудач: {stats['today']['failed']}
📋 Всего попыток: {stats['today']['total']}

📈 <b>За все время:</b>
✅ Отправлено: {stats['all_time']['sent']}
❌ Неудач: {stats['all_time']['failed']}
📋 Всего попыток: {stats['all_time']['total']}

⚡ <b>Текущая сессия:</b>
✅ Отправлено: {stats['current_session']['sent']}
❌ Неудач: {stats['current_session']['failed']}

🏆 <b>Глобальные итоги:</b>
✅ Всего отправлено: {stats['global_totals']['sent']}
❌ Всего неудач: {stats['global_totals']['failed']}

📱 <b>Уникальные номера:</b>
🎯 Получили SMS: {stats['unique_phones']}

            ⏳ <b>Очередь (вне рабочих часов):</b> {pending}

📈 <b>Общий процент успеха:</b> {(stats['all_time']['sent'] / (stats['all_time']['sent'] + stats['all_time']['failed']) * 100) if (stats['all_time']['sent'] + stats['all_time']['failed']) > 0 else 0:.1f}%"""

        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="⏳ Очередь SMS", callback_data="pending_sms_queue")],
            [InlineKeyboardButton(text="📜 Последние логи", callback_data="recent_sms_logs")],
            [InlineKeyboardButton(text="⬅️ К статистике", callback_data="stats_hub")]
        ])

        await call.message.answer(message, reply_markup=keyboard)
        
    except Exception as e:
        # Fallback на старую статистику в случае ошибки
        succ, fail = await get_totals()

        message = f"""📊 <b>SMS статистика (базовая)</b>

✅ Успешных отправок: {succ}
❌ Не успешных: {fail}
📋 Всего: {succ + fail}

<i>❗ Для детальной статистики требуется обновление системы</i>"""

        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="⬅️ К статистике", callback_data="stats_hub")]
        ])
        
        await call.message.answer(message, reply_markup=keyboard)


@router.callback_query(F.data == "recent_sms_logs")
async def recent_sms_logs(call: CallbackQuery):
    await safe_delete_message(call.message)
    
    try:
        from app.database.crud_static import get_recent_sms_logs
        recent_logs = await get_recent_sms_logs(limit=10)
        
        if not recent_logs:
            message = "📜 <b>Последние SMS логи</b>\n\n❌ Логи отсутствуют"
        else:
            lines = ["📜 <b>Последние 10 SMS логов:</b>\n"]
            
            for i, log in enumerate(recent_logs, 1):
                status_emoji = "✅" if log.is_sent else "❌"
                # Ограничиваем длину названия для читабельности
                name_short = (log.name[:80] + "...") if len(log.name) > 80 else log.name
                date_str = log.created_at.strftime("%d.%m %H:%M")
                
                lines.append(f"{i}. {status_emoji} <b>{date_str}</b>")
                lines.append(f"   {name_short}\n")
            
            message = "\n".join(lines)
            
            # Ограничиваем общую длину сообщения
            if len(message) > 4000:
                message = message[:4000] + "\n\n<i>... и другие логи</i>"
        
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="🔄 Обновить", callback_data="recent_sms_logs")],
            [InlineKeyboardButton(text="⬅️ К статистике", callback_data="staticsms")]
        ])
        
        await call.message.answer(message, reply_markup=keyboard)
        
    except Exception as e:
        message = f"❌ <b>Ошибка загрузки логов</b>\n\n<code>{str(e)}</code>"
        
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="⬅️ К статистике", callback_data="staticsms")]
        ])
        
        await call.message.answer(message, reply_markup=keyboard)


@router.callback_query(F.data == "backstartmenu")
async def backstart(call:CallbackQuery, state:FSMContext):
    await safe_delete_message(call.message)
    await call.message.answer(f"<b>Добро пожаловать в бота AutoRia</b>", reply_markup=start_kb())
    await state.clear()


# ================== SMS НАСТРОЙКИ ==================

@router.callback_query(F.data == "sms_settings")
async def sms_settings_menu(call: CallbackQuery):
    await safe_delete_message(call.message)
    
    try:
        from app.database.crud_sms import get_sms_settings
        settings = await get_sms_settings()
        
        current_text = settings.sms_text[:100] + "..." if len(settings.sms_text) > 100 else settings.sms_text
        current_sender = settings.sender_name
        
        message = f"""📱 <b>Настройки SMS</b>

📝 <b>Текущий текст:</b>
<code>{current_text}</code>

👤 <b>Отправитель:</b> <code>{current_sender}</code>

Выберите что хотите изменить:"""
        
        await call.message.answer(message, reply_markup=sms_settings_kb())
        
    except Exception as e:
        await call.message.answer(
            f"❌ Ошибка загрузки настроек SMS: {e}", 
            reply_markup=InlineKeyboardMarkup(inline_keyboard=[
                [InlineKeyboardButton(text="⬅️ К настройкам", callback_data="settings_hub")]
            ])
        )


@router.callback_query(F.data == "test_sms")
async def test_sms_start(call: CallbackQuery, state: FSMContext):
    await call.answer()
    hint = PARSING_START_SMS_TEST_NUMBER or "+380XXXXXXXXX"
    await state.set_state(SmsSettingsState.waiting_for_test_number)
    await call.message.answer(
        "📤 <b>Тест SMS</b>\n\n"
        "Отправьте номер в международном формате, например:\n"
        f"<code>{hint}</code>\n\n"
        "Или нажмите /start чтобы отменить.\n"
        f"Дневной лимит: "
        f"{'выкл' if SMS_DAILY_LIMIT <= 0 else str(SMS_DAILY_LIMIT)}"
    )


@router.message(SmsSettingsState.waiting_for_test_number)
async def test_sms_send(message: Message, state: FSMContext):
    from spam_sms import send_sms, clean_phone_number
    from app.database.crud_static import add_sms_log, count_today_sent_sms

    raw = (message.text or "").strip()
    cleaned = clean_phone_number(raw)
    if not cleaned:
        await message.answer(
            "⚠️ Не похоже на номер. Пример: <code>+380631234567</code>"
        )
        return

    if SMS_DAILY_LIMIT > 0:
        sent_today = await count_today_sent_sms()
        if sent_today >= SMS_DAILY_LIMIT:
            await state.clear()
            await message.answer(
                f"⏸ Дневной лимит SMS достигнут ({sent_today}/{SMS_DAILY_LIMIT}). "
                "Тест сегодня недоступен.",
                reply_markup=sms_settings_kb(),
            )
            try:
                from app.notify import notify_admin

                await notify_admin(
                    f"⏸ Тест SMS отклонён — лимит {sent_today}/{SMS_DAILY_LIMIT} "
                    f"(user {message.from_user.id})"
                )
            except Exception:
                pass
            return
        threshold_80 = int(SMS_DAILY_LIMIT * 0.8)
        if sent_today >= threshold_80:
            await message.answer(
                f"⚠️ Дневной лимит SMS на 80%: {sent_today}/{SMS_DAILY_LIMIT}."
            )
            try:
                from app.notify import notify_admin

                await notify_admin(
                    f"⚠️ Лимит SMS ~80%: {sent_today}/{SMS_DAILY_LIMIT} "
                    f"(тест, user {message.from_user.id})"
                )
            except Exception:
                pass

    await message.answer(f"⏳ Отправляю тест на <code>{cleaned}</code>...")
    result = await send_sms([cleaned])
    await state.clear()

    if result.get("success"):
        await add_sms_log(
            ad_name="Тест SMS (ручной)",
            link="test://manual",
            phones=[cleaned],
            is_sent=True,
        )
        await message.answer(
            f"✅ Тест SMS отправлен на <code>{cleaned}</code>",
            reply_markup=sms_settings_kb(),
        )
    else:
        await add_sms_log(
            ad_name="Тест SMS (ручной)",
            link="test://manual",
            phones=[cleaned],
            is_sent=False,
        )
        await message.answer(
            f"❌ Не удалось отправить: {result.get('error_message', 'неизвестно')}",
            reply_markup=sms_settings_kb(),
        )


@router.callback_query(F.data == "daily_report_now")
async def daily_report_now(call: CallbackQuery):
    await call.answer("Формирую отчёт...")
    from app.daily_report import send_daily_report

    text = await send_daily_report(force=True, to_user_id=int(call.from_user.id))
    if not text:
        await call.message.answer("Не удалось сформировать отчёт.")
    await call.message.answer("📈 Статистика:", reply_markup=stats_hub_kb())


# ================== ОЧЕРЕДЬ SMS ==================

_MAX_QUEUE_PREVIEW = 20


async def _build_pending_sms_queue_text() -> tuple[str, int]:
    from app.database.crud_pending_sms import count_pending_sms, list_pending_sms

    total = await count_pending_sms()
    rows = await list_pending_sms(limit=_MAX_QUEUE_PREVIEW)
    if total <= 0:
        return (
            "⏳ <b>Очередь SMS пуста</b>\n\n"
            "Новые SMS появятся здесь, если парсинг найдёт номера "
            "вне окна work_from–work_to.",
            0,
        )

    lines = [
        f"⏳ <b>Очередь SMS: {total}</b>",
        "",
        "Отправка по расписанию (дни/часы в настройках) или кнопкой ниже.",
        "",
    ]
    for i, row in enumerate(rows, 1):
        name = (row.ad_name or "Без названия")[:60]
        phone = row.phone_number or "—"
        ad_id = row.ad_id or "—"
        link = (row.link or "").strip()
        lines.append(f"<b>{i}.</b> {name}")
        lines.append(f"   🆔 <code>{ad_id}</code>")
        lines.append(f"   📞 <code>{phone}</code>")
        if link:
            lines.append(f"   🔗 {link}")
        lines.append("")

    if total > _MAX_QUEUE_PREVIEW:
        lines.append(f"<i>…и ещё {total - _MAX_QUEUE_PREVIEW} в очереди</i>")

    text = "\n".join(lines)
    if len(text) > 3900:
        text = text[:3900] + "\n…"
    return text, total


@router.callback_query(F.data == "pending_sms_queue")
async def pending_sms_queue(call: CallbackQuery):
    await call.answer()
    await safe_delete_message(call.message)
    text, total = await _build_pending_sms_queue_text()
    await call.message.answer(
        text,
        reply_markup=pending_sms_queue_kb(has_items=total > 0),
        disable_web_page_preview=True,
    )


@router.callback_query(F.data == "pending_sms_flush_ask")
async def pending_sms_flush_ask(call: CallbackQuery):
    await call.answer()
    from app.database.crud_pending_sms import count_pending_sms

    total = await count_pending_sms()
    if total <= 0:
        await call.message.answer(
            "⏳ Очередь пуста.",
            reply_markup=pending_sms_queue_kb(has_items=False),
        )
        return

    await call.message.answer(
        f"🚀 <b>Отправить всю очередь сейчас?</b>\n\n"
        f"В очереди: <b>{total}</b> SMS.\n"
        f"Окно расписания будет <b>проигнорировано</b>.\n"
        f"Между SMS — короткая пауза ~2 сек.\n\n"
        f"Продолжить?",
        reply_markup=pending_sms_flush_confirm_kb(),
    )


@router.callback_query(F.data == "pending_sms_flush_now")
async def pending_sms_flush_now(call: CallbackQuery):
    await call.answer()
    from app.database.crud_pending_sms import count_pending_sms
    from app.pending_sms_worker import flush_pending_sms, _flush_lock

    if _flush_lock.locked():
        await call.message.answer("⏳ Уже идёт отправка очереди. Подождите.")
        return

    total = await count_pending_sms()
    if total <= 0:
        await call.message.answer(
            "⏳ Очередь пуста.",
            reply_markup=pending_sms_queue_kb(has_items=False),
        )
        return

    await call.message.answer(
        f"🚀 Отправляю очередь ({total} SMS)…\n"
        f"Это может занять ~{max(total * 3, 5)} сек."
    )

    try:
        from app.notify import notify_admin

        await notify_admin(
            f"🚀 Ручная отправка очереди SMS: {total} шт.\n"
            f"user_id={call.from_user.id}"
        )
    except Exception:
        pass

    from app.pending_sms_worker import format_queue_summary

    stats = await flush_pending_sms(
        force=True,
        max_items=max(total, 50),
        pause_sec=2,
        notify_summary=False,
    )

    left = await count_pending_sms()
    await call.message.answer(
        format_queue_summary(stats, left=left, force=True),
        reply_markup=pending_sms_queue_kb(has_items=left > 0),
    )


@router.callback_query(F.data == "view_sms_settings")
async def view_sms_settings(call: CallbackQuery):
    await safe_delete_message(call.message)
    
    try:
        from app.database.crud_sms import get_sms_settings
        settings = await get_sms_settings()
        
        message = f"""👀 <b>Полные настройки SMS</b>

📝 <b>Текст сообщения:</b>
<code>{settings.sms_text}</code>

👤 <b>Имя отправителя:</b>
<code>{settings.sender_name}</code>

💡 <i>Этот текст будет отправляться на все найденные номера телефонов</i>"""
        
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="✏️ Изменить текст", callback_data="change_sms_text")],
            [InlineKeyboardButton(text="📝 Изменить отправителя", callback_data="change_sms_sender")],
            [InlineKeyboardButton(text="⬅️ Назад", callback_data="sms_settings")]
        ])
        
        await call.message.answer(message, reply_markup=keyboard)
        
    except Exception as e:
        await call.message.answer(f"❌ Ошибка: {e}")


@router.callback_query(F.data == "change_sms_text")
async def change_sms_text(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    
    try:
        from app.database.crud_sms import get_sms_settings
        settings = await get_sms_settings()
        
        message = f"""✏️ <b>Изменение текста SMS</b>

📝 <b>Текущий текст:</b>
<code>{settings.sms_text}</code>

Введите новый текст SMS сообщения:"""
        
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="⬅️ Отмена", callback_data="sms_settings")]
        ])
        
        await call.message.answer(message, reply_markup=keyboard)
        await state.set_state(SmsSettingsState.waiting_for_sms_text)
        
    except Exception as e:
        await call.message.answer(f"❌ Ошибка: {e}")


@router.callback_query(F.data == "change_sms_sender")
async def change_sms_sender(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    
    try:
        from app.database.crud_sms import get_sms_settings
        settings = await get_sms_settings()
        
        message = f"""📝 <b>Изменение отправителя SMS</b>

👤 <b>Текущий отправитель:</b>
<code>{settings.sender_name}</code>

⚠️ <b>Важно:</b> Имя отправителя должно быть зарегистрировано в системе TurboSMS!

Введите новое имя отправителя:"""
        
        keyboard = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="⬅️ Отмена", callback_data="sms_settings")]
        ])
        
        await call.message.answer(message, reply_markup=keyboard)
        await state.set_state(SmsSettingsState.waiting_for_sender_name)
        
    except Exception as e:
        await call.message.answer(f"❌ Ошибка: {e}")


@router.message(SmsSettingsState.waiting_for_sms_text)
async def process_new_sms_text(message: Message, state: FSMContext):
    new_text = message.text.strip()
    
    if len(new_text) > 1000:
        await message.answer("❌ Текст слишком длинный! Максимум 1000 символов.")
        return
    
    if len(new_text) < 1:
        await message.answer("❌ Текст не может быть пустым!")
        return
    
    try:
        from app.database.crud_sms import update_sms_text
        await update_sms_text(new_text)
        
        preview = new_text[:200] + "..." if len(new_text) > 200 else new_text
        
        await message.answer(
            f"✅ <b>Текст SMS успешно обновлен!</b>\n\n"
            f"📝 <b>Новый текст:</b>\n<code>{preview}</code>", 
            reply_markup=sms_settings_kb()
        )
        
        await state.clear()
        
    except Exception as e:
        await message.answer(f"❌ Ошибка сохранения: {e}")


# ================== УДАЛЕНИЕ СТАТИСТИКИ ==================

@router.callback_query(F.data == "clear_stats_menu")
async def clear_stats_menu(call: CallbackQuery, state: FSMContext):
    await safe_delete_message(call.message)
    await state.set_state(DeleteStatsState.selecting_dates)
    await state.update_data(selected_dates=[]) # Initialize empty list
    
    kb, text = await get_autoinfo_unique_dates(for_selection=True, selected_dates=[])
    await call.message.answer(f"<b>{text}</b>", reply_markup=kb)


@router.callback_query(DeleteStatsState.selecting_dates, F.data.startswith("select_stat_"))
async def toggle_stat_date(call: CallbackQuery, state: FSMContext):
    date_str = call.data.split("select_stat_")[1]
    data = await state.get_data()
    selected_dates = data.get("selected_dates", [])
    
    if date_str in selected_dates:
        selected_dates.remove(date_str)
    else:
        selected_dates.append(date_str)
    
    await state.update_data(selected_dates=selected_dates)
    
    # Refresh keyboard
    kb, text = await get_autoinfo_unique_dates(for_selection=True, selected_dates=selected_dates)
    
    try:
        await call.message.edit_text(f"<b>{text}</b>", reply_markup=kb)
    except Exception:
        pass
    
    await call.answer()


@router.callback_query(DeleteStatsState.selecting_dates, F.data == "confirm_delete_stats")
async def confirm_delete_stats(call: CallbackQuery, state: FSMContext):
    data = await state.get_data()
    selected_dates = data.get("selected_dates", [])
    
    if not selected_dates:
        await call.answer("Выберите хотя бы одну дату!", show_alert=True)
        return
        
    await safe_delete_message(call.message)
    
    dates_str = "\n".join(sorted(selected_dates))
    
    kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="✅ Да, удалить", callback_data="perform_delete_stats")],
        [InlineKeyboardButton(text="❌ Нет, назад", callback_data="clear_stats_menu")]
    ])
    
    await call.message.answer(
        f"⚠️ <b>Вы уверены, что хотите удалить статистику за эти даты?</b>\n\n{dates_str}",
        reply_markup=kb
    )


@router.callback_query(DeleteStatsState.selecting_dates, F.data == "perform_delete_stats")
async def perform_delete_stats(call: CallbackQuery, state: FSMContext):
    data = await state.get_data()
    selected_dates = data.get("selected_dates", [])
    
    if selected_dates:
        await delete_autoinfo_by_dates(selected_dates)
        await call.answer("Статистика удалена ✅", show_alert=True)
    
    await state.clear()
    
    # Return to main stats menu
    await safe_delete_message(call.message)
    kb, text = await get_autoinfo_unique_dates()
    await call.message.answer(f"<b>{text}</b>", reply_markup=kb)


@router.message(SmsSettingsState.waiting_for_sender_name)
async def process_new_sender_name(message: Message, state: FSMContext):
    new_sender = message.text.strip()
    
    if len(new_sender) > 50:
        await message.answer("❌ Имя отправителя слишком длинное! Максимум 50 символов.")
        return
    
    if len(new_sender) < 1:
        await message.answer("❌ Имя отправителя не может быть пустым!")
        return
    
    # Простая проверка на валидные символы (буквы, цифры, _, -)
    import re
    if not re.match(r'^[a-zA-Z0-9_-]+$', new_sender):
        await message.answer("❌ Имя отправителя может содержать только буквы, цифры, _ и -")
        return
    
    try:
        from app.database.crud_sms import update_sender_name
        await update_sender_name(new_sender)
        
        await message.answer(
            f"✅ <b>Имя отправителя успешно обновлено!</b>\n\n"
            f"👤 <b>Новый отправитель:</b> <code>{new_sender}</code>", 
            reply_markup=sms_settings_kb()
        )
        
        await state.clear()
        
    except Exception as e:
        await message.answer(f"❌ Ошибка сохранения: {e}")



