"""
Обработчики просмотра заявок (Intradesk API).

Заявки загружаются из Intradesk API по нажатию кнопки — только незакрытые.
Навигация через callback payload:
  "view_tickets"          → список, страница 0
  "itickets_page:{n}"    → список, страница n
  "iticket_detail:{id}"  → карточка задачи (из кэша в context)
  "main_menu"            → в menu.py
"""
from __future__ import annotations

import re
import time

from maxapi.context import BaseContext
from maxapi.dispatcher import Router
from maxapi.filters import F
from maxapi.types.updates.message_callback import MessageCallback

from bot.filters import PayloadStartsWith
from bot.keyboards import iticket_detail_kb, main_menu_kb, menu_only_kb
from bot.send_helper import bot_reply, bot_send
from bot.states import CommentStates
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 external.intradesk_api import (
    add_task_comment,
    get_client_open_tasks,
    get_task_last_comment,
    invalidate_client_cache,
    invalidate_task_comment,
)

router = Router(router_id="view_tickets")

_PAGE_SIZE = 5


def _status_icon(status_name: str) -> str:
    sn = status_name.lower()
    if "выполнен" in sn or "закрыт" in sn:
        return "🟢"
    if "работ" in sn:
        return "🟡"
    if "открыт" in sn:
        return "🔴"
    return "⚪"


def _fmt_date(iso: str) -> str:
    """'2026-03-26T09:57:11Z' → '26.03.2026 09:57'"""
    if not iso or len(iso) < 10:
        return iso or "—"
    y, m, d = iso[:10].split("-")
    # iso может быть "2026-03-26T09:57:11Z" или "2026-03-26 09:57"
    time_part = iso[11:16] if len(iso) >= 16 else ""
    return f"{d}.{m}.{y}" + (f" {time_part}" if time_part else "")


def _fmt_date_short(iso: str) -> str:
    """'2026-03-26 11:54' → '26.03 11:54'"""
    if not iso or len(iso) < 10:
        return iso or ""
    y, m, d = iso[:10].split("-")
    time_part = iso[11:16] if len(iso) >= 16 else ""
    return f"{d}.{m}" + (f" {time_part}" if time_part else "")


def _build_tasks_text(tasks: list[dict], page: int, total: int) -> str:
    pages = max(1, -(-total // _PAGE_SIZE))
    lines = [f"📋 Активные заявки (всего: {total}) — стр. {page + 1}/{pages}"]

    for t in tasks:
        icon = _status_icon(t.get("status_name") or "")
        num = t.get("task_number") or t.get("id") or "?"
        name = (t.get("name") or "—")[:60]
        created = _fmt_date(t.get("created_at") or "")
        status = t.get("status_name") or "—"

        lines += [
            "",
            "─" * 20,
            f"📌 №{num} · {created}",
            f"{name}",
            f"{icon} {status}",
        ]

        comment = t.get("last_comment")
        if comment and comment.get("text"):
            ctext = comment["text"][:80] + ("…" if len(comment["text"]) > 80 else "")
            author_short = (comment.get("author") or "—").split()[0]
            cdate = _fmt_date_short(comment.get("date") or "")
            lines.append(f'💬 "{ctext}" — {author_short} ({cdate})')
        else:
            lines.append("💬 Нет комментариев")

    return "\n".join(lines)


async def _load_and_show(
    event: MessageCallback,
    bot_user: dict,
    page: int,
    context: BaseContext,
) -> None:
    """
    Загрузить страницу заявок из API и показать.

    Сразу редактирует сообщение на «⏳ Загрузка...», затем делает запрос к API.
    Перед рендером проверяет load_token — если пользователь успел уйти
    в другой флоу, контекст изменится и мы тихо выходим.
    """
    org_id = bot_user.get("org_id")
    intradesk_client_id: int | None = None
    if org_id:
        org = await get_organization(org_id)
        intradesk_client_id = org.get("intradesk_id") if org else None

    # Уникальный токен для этого запроса
    token = str(time.monotonic_ns())
    mid = event.message.body.mid if (event.message and event.message.body) else None
    chat_id = event.message.recipient.chat_id if event.message else None

    # Записываем токен в контекст и сразу показываем загрузку
    await context.set_data({"load_token": token})
    await bot_reply(
        event,
        text="⏳ Загружаем список заявок...",
        attachments=None,
        user_id=bot_user["max_user_id"],
        chat_id=chat_id,
    )

    if not intradesk_client_id:
        # Проверяем токен перед рендером
        data = await context.get_data()
        if data.get("load_token") != token:
            return
        await _edit_or_send(event.bot, mid, chat_id,
            text="⚠️ Ваша организация не привязана к Intradesk. Обратитесь к администратору.",
            attachments=[menu_only_kb()])
        return

    # Запрос к API
    result = await get_client_open_tasks(intradesk_client_id, page=page, page_size=_PAGE_SIZE)
    tasks = result["tasks"]
    total = result["total"]

    # Проверяем токен — не ушёл ли пользователь пока грузили
    data = await context.get_data()
    if data.get("load_token") != token:
        return  # Пользователь уже в другом флоу — не трогаем его экран

    # Сохраняем задачи в контексте для детального просмотра
    await context.set_data({"tasks": tasks, "page": page, "total": total})

    if not tasks:
        await _edit_or_send(event.bot, mid, chat_id,
            text="📭 Активных заявок нет.",
            attachments=[menu_only_kb()])
        return

    from bot.keyboards import intradesk_tasks_list_kb
    text = _build_tasks_text(tasks, page, total)
    kb = intradesk_tasks_list_kb(tasks, page=page, total=total, page_size=_PAGE_SIZE)
    await _edit_or_send(event.bot, mid, chat_id, text=text, attachments=[kb])


async def _edit_or_send(bot, mid: str | None, chat_id: int | None, *, text: str, attachments) -> None:
    """Редактировать сообщение по mid или отправить новое если mid недоступен."""
    if mid:
        try:
            await bot.edit_message(
                message_id=mid,
                text=text,
                attachments=attachments if attachments is not None else [],
            )
            return
        except Exception:
            pass
    if chat_id:
        await bot.send_message(chat_id=chat_id, text=text, attachments=attachments or [])


@router.message_callback(F.callback.payload == "view_tickets")
async def cb_view_tickets(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    from bot.handlers.create_ticket import cleanup_ticket_flow
    await cleanup_ticket_flow(event.bot, context)
    await event.answer(notification="")
    await _load_and_show(event, bot_user, page=0, context=context)


@router.message_callback(PayloadStartsWith("itickets_page:"))
async def cb_itickets_page(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    await event.answer(notification="")
    try:
        page = int(event.callback.payload.split(":")[1])
    except (IndexError, ValueError):
        page = 0
    await _load_and_show(event, bot_user, page=page, context=context)


@router.message_callback(PayloadStartsWith("iticket_detail:"))
async def cb_iticket_detail(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    try:
        task_id = int(event.callback.payload.split(":")[1])
    except (IndexError, ValueError):
        await event.answer(notification="Ошибка")
        return

    await event.answer(notification="")

    # Ищем задачу в кэше контекста (загружена при показе списка)
    data = await context.get_data()
    task = next((t for t in (data.get("tasks") or []) if t.get("id") == task_id), None)

    if task is None:
        await bot_reply(
            event,
            text="⚠️ Заявка не найдена. Вернитесь к списку.",
            attachments=[iticket_detail_kb()],
            user_id=bot_user["max_user_id"],
            chat_id=event.message.recipient.chat_id if event.message else None,
        )
        return

    icon = _status_icon(task.get("status_name") or "")
    num = task.get("task_number") or task["id"]

    lines = [
        f"📌 Заявка #{num}",
        f"Статус: {icon} {task.get('status_name') or '—'}",
        f"Создана: {_fmt_date(task.get('created_at') or '')}",
        f"Обновлена: {_fmt_date(task.get('updated_at') or '')}",
    ]
    if task.get("name"):
        lines += ["", f"📄 {task['name']}"]

    # Последний комментарий — отдельный запрос к history API
    comment = await get_task_last_comment(task["id"])
    if comment and comment.get("text"):
        ctext = comment["text"]
        if len(ctext) > 500:
            ctext = ctext[:500] + "…"
        lines += [
            "",
            f"💭 Последний комментарий ({comment['date']}):",
            f"    {ctext}",
            f"    — {comment['author']}",
        ]

    await bot_reply(
        event,
        text="\n".join(lines),
        attachments=[iticket_detail_kb(task_id=task["id"])],
        user_id=bot_user["max_user_id"],
        chat_id=event.message.recipient.chat_id if event.message else None,
    )


# ── Оставить комментарий ──────────────────────────────────────────────────────

@router.message_callback(PayloadStartsWith("iticket_comment:"))
async def cb_add_comment(
    event: MessageCallback, context: BaseContext, bot_user: dict
) -> None:
    try:
        task_id = int(event.callback.payload.split(":")[1])
    except (IndexError, ValueError):
        await event.answer(notification="Ошибка")
        return

    await event.answer(notification="")

    # Ищем task_number в кэше контекста
    data = await context.get_data()
    task = next((t for t in (data.get("tasks") or []) if t.get("id") == task_id), None)
    task_number = task.get("task_number") if task else None

    await context.set_state(CommentStates.waiting_text)
    await context.set_data({
        "comment_task_id": task_id,
        "comment_task_number": task_number,
        "flow_message_ids": [],
    })

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

    num_str = f" к заявке #{task_number}" if task_number else ""
    mid = await bot_send(
        event.bot,
        chat_id=chat_id,
        user_id=bot_user["max_user_id"],
        text=(
            f"💬 Напишите комментарий{num_str}:\n\n"
            "Для отмены нажмите «🏠 Главное меню»."
        ),
        attachments=[menu_only_kb()],
    )
    # Сохраняем mid для возможной очистки
    if mid:
        d = await context.get_data()
        d["flow_message_ids"] = [mid]
        await context.set_data(d)


@router.message_created(states=CommentStates.waiting_text)
async def on_comment_text(
    event: MessageCreated, context: BaseContext, bot_user: dict
) -> 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 ""
    if not text:
        await bot_send(
            event.bot,
            chat_id=chat_id,
            user_id=user_id,
            text="❗ Комментарий не может быть пустым. Напишите текст:",
            attachments=[menu_only_kb()],
        )
        return

    data = await context.get_data()
    task_id = data.get("comment_task_id")
    task_number = data.get("comment_task_number")

    # Сбрасываем состояние до отправки API — защита от повторов
    await context.clear()

    # Получаем данные организации (нужны и для contact_person_id, и для инвалидации кэша)
    intradesk_id: int | None = None
    contact_person_id: int | None = None
    if bot_user.get("org_id"):
        org = await get_organization(bot_user["org_id"])
        intradesk_id = org.get("intradesk_id") if org else 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"]
            )

    ok = await add_task_comment(
        task_number=task_number,
        text=text,
        contact_person_id=contact_person_id,
    )

    if ok:
        # Сбрасываем кэш — следующий переход в меню/список покажет свежие данные
        if task_id:
            invalidate_task_comment(task_id)
        if intradesk_id:
            invalidate_client_cache(intradesk_id)

    from bot.handlers.menu import build_main_menu_text

    if ok:
        num_str = f" к заявке #{task_number}" if task_number else ""
        await bot_send(
            event.bot,
            chat_id=chat_id,
            user_id=user_id,
            text=f"✅ Комментарий{num_str} успешно отправлен!",
        )
    else:
        await bot_send(
            event.bot,
            chat_id=chat_id,
            user_id=user_id,
            text="⚠️ Не удалось отправить комментарий. Попробуйте позже.",
        )

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