"""
Обработчики создания заявки.

Поток:
1. "Создать заявку"  → waiting_description
2. Первое сообщение  → сохранение описания, collecting_attachments
3. Доп. сообщения    → накопление вложений
4. "Готово"          → предпросмотр + кнопки подтверждения, confirming
5a. "Подтвердить"    → сохранение в БД + Intradesk, сообщение с номером, главное меню
5b. "Продолжить редактирование" → обратно в collecting_attachments
5c. "Отменить создание" → очистка состояния, главное меню
"""
from maxapi.context import BaseContext
from maxapi.dispatcher import Router
from maxapi.enums.attachment import AttachmentType
from maxapi.filters import F
from maxapi.types.attachments.attachment import (
    OtherAttachmentPayload,
    PhotoAttachmentPayload,
)
from maxapi.types.updates.message_callback import MessageCallback
from maxapi.types.updates.message_created import MessageCreated

from bot.handlers.menu import build_main_menu_text
from bot.keyboards import confirm_ticket_kb, done_ticket_kb, main_menu_kb
from bot.logging_setup import get_logger
from bot.send_helper import bot_send
from bot.states import TicketStates
from database.connection import get_db
from database.queries.intradesk import get_intradesk_contact_id_for_phone
from database.queries.organizations import get_organization
from database.queries.ticket_attachments import add_attachment
from database.queries.tickets import create_ticket, set_intradesk_task_id
from external.intradesk_api import create_intradesk_task

logger = get_logger("bot.handlers.create_ticket")

router = Router(router_id="create_ticket")


async def cleanup_ticket_flow(bot, context: BaseContext) -> None:
    """
    Удалить висящие сообщения незавершённого потока создания заявки.
    Вызывается из любого обработчика, который прерывает этот поток
    (главное меню, список заявок, повторное нажатие «Создать заявку»).
    """
    from bot.states import TicketStates, CommentStates  # avoid circular at module level
    state = await context.get_state()
    creation_states = [
        TicketStates.waiting_description,
        TicketStates.collecting_attachments,
        TicketStates.confirming,
        CommentStates.waiting_text,
    ]
    if state not in creation_states:
        return
    data = await context.get_data()
    for mid in (data.get("flow_message_ids") or []):
        try:
            await bot.delete_message(message_id=mid)
        except Exception:
            pass
    await context.clear()


async def _track_mid(context: BaseContext, mid: str | None) -> None:
    """Добавить message_id в список сообщений потока создания."""
    if not mid:
        return
    data = await context.get_data()
    ids: list[str] = data.get("flow_message_ids") or []
    ids.append(mid)
    data["flow_message_ids"] = ids
    await context.set_data(data)


async def _delete_flow_messages(bot, context: BaseContext, extra_mid: str | None = None) -> None:
    """Удалить все сообщения, накопленные за время потока создания."""
    data = await context.get_data()
    ids: list[str] = list(data.get("flow_message_ids") or [])
    if extra_mid:
        ids.append(extra_mid)
    for mid in ids:
        try:
            await bot.delete_message(message_id=mid)
        except Exception:
            pass  # сообщение уже удалено или слишком старое

_MEDIA_TYPES = {
    AttachmentType.IMAGE,
    AttachmentType.VIDEO,
    AttachmentType.AUDIO,
    AttachmentType.FILE,
}


def _extract_attachments(event: MessageCreated) -> list[dict]:
    """Извлекает метаданные вложений из сообщения."""
    result = []
    body = event.message.body
    if not body or not body.attachments:
        return result
    for att in body.attachments:
        att_type = getattr(att, "type", None)
        if att_type not in _MEDIA_TYPES:
            continue
        url: str | None = None
        filename: str | None = None
        duration: int | None = None
        payload = getattr(att, "payload", None)
        if isinstance(payload, PhotoAttachmentPayload):
            url = payload.url
        elif isinstance(payload, OtherAttachmentPayload):
            url = payload.url
            filename = (
                getattr(payload, "filename", None)
                or getattr(payload, "name", None)
                or getattr(payload, "title", None)
            )
        if att_type == AttachmentType.AUDIO:
            duration = (
                getattr(payload, "duration", None)
                or getattr(payload, "duration_ms", None)
            )
        result.append({"type": str(att_type), "url": url, "filename": filename, "duration": duration})
    return result


def _att_label(att: dict) -> str:
    """Человекочитаемое описание вложения для предпросмотра."""
    t = att.get("type", "")
    if AttachmentType.IMAGE.value in t or "PHOTO" in t.upper():
        return "🖼 Фото"
    if AttachmentType.VIDEO.value in t:
        return "📹 Видео"
    if AttachmentType.AUDIO.value in t:
        dur = att.get("duration")
        if dur:
            mins, secs = divmod(int(dur), 60)
            return f"🎤 Голосовое ({mins:02d}:{secs:02d})"
        return "🎤 Голосовое"
    if AttachmentType.FILE.value in t:
        fname = att.get("filename")
        return f"📎 {fname}" if fname else "📎 Файл"
    return "📎 Вложение"


def _preview_text(org_name: str | None, description: str, pending_atts: list[dict]) -> str:
    """Формирует текст предпросмотра заявки."""
    task_name = org_name or description[:80]
    lines = [
        "📋 Предпросмотр заявки:",
        "",
        f"📄 {task_name}",
        "",
        description[:500] + ("…" if len(description) > 500 else ""),
    ]
    if pending_atts:
        lines += ["", "📎 Вложения:"]
        for att in pending_atts:
            lines.append(f"  • {_att_label(att)}")
    lines += ["", "Всё верно? Подтвердите или вернитесь к редактированию."]
    return "\n".join(lines)


# ── Шаг 1: начало создания ────────────────────────────────────────────────────

@router.message_callback(F.callback.payload == "create_ticket")
async def cb_create_ticket(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    # Зачищаем предыдущий незавершённый поток, если был
    await cleanup_ticket_flow(event.bot, context)

    await context.set_state(TicketStates.waiting_description)
    await context.set_data({"pending_attachments": [], "flow_message_ids": []})
    logger.info("📝 create_ticket started | user_id=%s", bot_user["max_user_id"])
    await event.answer(notification="")

    chat_id = event.message.recipient.chat_id if event.message else None
    if chat_id is None:
        return

    mid = await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=bot_user["max_user_id"],
        text=(
            "📝 Опишите вашу проблему.\n\n"
            "Вы также можете прикрепить файлы, фото или голосовые сообщения.\n"
            "Когда закончите — нажмите кнопку «Готово»."
        ),
        attachments=[done_ticket_kb()],
    )
    await _track_mid(context, mid)


# ── Шаг 2: первое сообщение с описанием ──────────────────────────────────────

@router.message_created(states=TicketStates.waiting_description)
async def on_ticket_description(
    event: MessageCreated, context: BaseContext
) -> None:
    chat_id, user_id = event.get_ids()
    if chat_id is None:
        return

    body = event.message.body
    text = (body.text or "").strip() if body else ""
    attachments = _extract_attachments(event)

    await context.update_data(description=text, pending_attachments=attachments)
    await context.set_state(TicketStates.collecting_attachments)

    mid = await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=user_id,
        text=(
            "✏️ Описание принято. Вы можете добавить ещё файлы "
            "или нажмите «Готово» для отправки заявки."
        ),
        attachments=[done_ticket_kb()],
    )
    await _track_mid(context, mid)


# ── Шаг 3: дополнительные вложения/текст ─────────────────────────────────────

@router.message_created(states=TicketStates.collecting_attachments)
async def on_ticket_attachment(
    event: MessageCreated, context: BaseContext
) -> None:
    data = await context.get_data()
    body = event.message.body
    extra_text = (body.text or "").strip() if body else ""
    new_atts = _extract_attachments(event)

    if extra_text:
        current = data.get("description", "")
        data["description"] = (current + "\n" + extra_text).strip()

    pending = data.get("pending_attachments", [])
    pending.extend(new_atts)
    data["pending_attachments"] = pending
    await context.set_data(data)


# ── Шаг 4: "Готово" → предпросмотр ───────────────────────────────────────────

@router.message_callback(
    F.callback.payload == "done_ticket",
    states=[TicketStates.waiting_description, TicketStates.collecting_attachments],
)
async def cb_done_ticket(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    data = await context.get_data()
    description = (data.get("description") or "").strip()

    chat_id = event.message.recipient.chat_id if event.message else None
    if chat_id is None:
        return

    if not description:
        await event.answer(notification="Сначала опишите проблему!")
        await bot_send(
            event.bot,
            chat_id=chat_id,
            user_id=bot_user["max_user_id"],
            text="❗ Пожалуйста, сначала напишите описание проблемы.",
            attachments=[done_ticket_kb()],
        )
        return

    await event.answer(notification="")
    pending_atts = data.get("pending_attachments", [])

    org = await get_organization(bot_user["org_id"])
    org_name = org["name"] if org else None

    await context.set_state(TicketStates.confirming)

    mid = await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=bot_user["max_user_id"],
        text=_preview_text(org_name, description, pending_atts),
        attachments=[confirm_ticket_kb()],
    )
    await _track_mid(context, mid)


# ── Шаг 5б: "Продолжить редактирование" ──────────────────────────────────────

@router.message_callback(
    F.callback.payload == "edit_ticket",
    states=TicketStates.confirming,
)
async def cb_edit_ticket(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    await event.answer(notification="")
    await context.set_state(TicketStates.collecting_attachments)

    chat_id = event.message.recipient.chat_id if event.message else None
    if chat_id is None:
        return

    mid = await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=bot_user["max_user_id"],
        text=(
            "✏️ Продолжайте добавлять описание или вложения.\n"
            "Нажмите «Готово» когда будете готовы."
        ),
        attachments=[done_ticket_kb()],
    )
    await _track_mid(context, mid)


# ── Шаг 5в: "Отменить создание" ──────────────────────────────────────────────

@router.message_callback(
    F.callback.payload == "cancel_ticket",
    states=TicketStates.confirming,
)
async def cb_cancel_ticket(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    await event.answer(notification="Создание отменено")

    # Удаляем все сообщения потока + текущее сообщение с кнопками
    callback_mid = event.message.body.mid if (event.message and event.message.body) else None
    await _delete_flow_messages(event.bot, context, extra_mid=callback_mid)
    await context.clear()

    chat_id = event.message.recipient.chat_id if event.message else None
    if chat_id is None:
        return

    await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=bot_user["max_user_id"],
        text=await build_main_menu_text(bot_user),
        attachments=[main_menu_kb()],
    )


# ── Шаг 5а: "Подтвердить" → создание ─────────────────────────────────────────

@router.message_callback(
    F.callback.payload == "confirm_ticket",
    states=TicketStates.confirming,
)
async def cb_confirm_ticket(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    # Защита от двойного нажатия: читаем данные и сбрасываем состояние атомарно.
    # Повторный tap уже не пройдёт фильтр states=confirming.
    data = await context.get_data()
    # Удаляем все сообщения потока + текущее (предпросмотр с кнопками)
    callback_mid = event.message.body.mid if (event.message and event.message.body) else None
    await _delete_flow_messages(event.bot, context, extra_mid=callback_mid)
    await context.clear()

    description = (data.get("description") or "").strip()
    pending_atts = data.get("pending_attachments", [])

    chat_id = event.message.recipient.chat_id if event.message else None
    if chat_id is None:
        return

    await event.answer(notification="Отправляем заявку…")

    # ── Данные организации ────────────────────────────────────────────────────
    org = await get_organization(bot_user["org_id"])
    org_name = org["name"] if org else None
    intradesk_id: int | None = org.get("intradesk_id") if org else None

    contact_person_id: int | None = None
    if intradesk_id and bot_user.get("phone"):
        db = get_db()
        contact_person_id = await get_intradesk_contact_id_for_phone(
            db, intradesk_id, bot_user["phone"]
        )

    # ── Сохраняем локально ────────────────────────────────────────────────────
    ticket_id = await create_ticket(
        org_id=bot_user["org_id"],
        created_by=bot_user["id"],
        description=description,
    )
    for att in pending_atts:
        await add_attachment(
            ticket_id=ticket_id,
            attachment_type=att["type"],
            attachment_url=att.get("url"),
        )
    logger.info(
        "✅ ticket created | ticket_id=%s user_id=%s org_id=%s attachments=%s",
        ticket_id, bot_user["max_user_id"], bot_user["org_id"], len(pending_atts),
    )

    # ── Отправка в Intradesk (ждём, чтобы получить номер) ────────────────────
    task_name = org_name or description[:150]
    att_urls = [a["url"] for a in pending_atts if a.get("url")]

    intradesk_result = await create_intradesk_task(
        name=task_name,
        description=description,
        client_intradesk_id=intradesk_id,
        contact_person_id=contact_person_id,
        attachment_urls=att_urls or None,
    )

    intradesk_number: int | None = None
    if intradesk_result:
        iid = (
            intradesk_result.get("id") or intradesk_result.get("Id")
            or intradesk_result.get("ID")
        )
        intradesk_number = (
            intradesk_result.get("tasknumber") or intradesk_result.get("number")
            or intradesk_result.get("Number") or intradesk_result.get("TaskNumber")
        )
        if iid:
            await set_intradesk_task_id(ticket_id, int(iid))
            logger.info(
                "Intradesk task created: id=%s number=%s for ticket_id=%s",
                iid, intradesk_number, ticket_id,
            )

    # ── Итоговое сообщение ────────────────────────────────────────────────────
    lines = ["✅ Заявка успешно создана!"]
    if intradesk_number:
        lines.append(f"Номер заявки: #{intradesk_number}")
    lines += ["", f"📄 {task_name}", ""]
    lines.append(description[:500] + ("…" if len(description) > 500 else ""))
    if pending_atts:
        lines += ["", "📎 Вложения:"]
        for att in pending_atts:
            lines.append(f"  • {_att_label(att)}")
    lines += ["", "Наши специалисты свяжутся с вами в ближайшее время."]

    await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=bot_user["max_user_id"],
        text="\n".join(lines),
    )

    await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=bot_user["max_user_id"],
        text=await build_main_menu_text(bot_user),
        attachments=[main_menu_kb()],
    )
