# app/src/telegram/handlers/journal_ui.py

from __future__ import annotations

from datetime import datetime
from html import escape
from zoneinfo import ZoneInfo

from aiogram.types import InlineKeyboardMarkup
from aiogram.utils.keyboard import InlineKeyboardBuilder

from src.core.config import load_settings
from src.core.event_titles import event_title
from src.core.numbers import safe_float
from src.core.types import JsonDict, JsonList, NumericLike
from src.trading.journal.filters import journal_export_filter_label


PAGE_SIZE = 3

LEVEL_ICONS = {
    "INFO": "ℹ️",
    "WARNING": "⚠️",
    "ERROR": "⛔",
    "CRITICAL": "🆘",
}

TECH_TO_HUMAN_MESSAGES = {
    "invalid api key": "Неверный API Key.",
    "unauthorized": "Нет доступа к аккаунту.",
    "forbidden": "Доступ запрещён.",
    "network error": "Нет связи с биржей.",
    "timeout": "Биржа не ответила вовремя.",
}


def build_keyboard(
    page: int,
    total_pages: int,
) -> InlineKeyboardMarkup:
    current_page = max(1, int(page))
    pages_count = max(1, int(total_pages))

    kb = InlineKeyboardBuilder()

    if current_page > 1:
        kb.button(text="⏮️", callback_data="journal:1")
        kb.button(text="⬅️", callback_data=f"journal:{current_page - 1}")

    kb.button(text=f"{current_page}/{pages_count}", callback_data="journal:noop")

    if current_page < pages_count:
        kb.button(text="➡️", callback_data=f"journal:{current_page + 1}")

    kb.button(text="📤 Экспорт", callback_data="journal:actions")
    kb.button(text="🛠️ Настройки", callback_data="settings:journal")
    kb.button(text="⬅️ Назад", callback_data="system:back")

    nav_count = 1

    if current_page > 1:
        nav_count += 2

    if current_page < pages_count:
        nav_count += 1

    kb.adjust(nav_count, 2, 1)
    return kb.as_markup()


def _html(value: object) -> str:
    return escape(str(value or ""), quote=False)


def build_actions_keyboard() -> InlineKeyboardMarkup:
    # Первый экран экспорта: выбираем, что именно экспортировать.
    kb = InlineKeyboardBuilder()

    kb.button(text="🤖 Автоторговля", callback_data="journal:export_filter:auto")
    kb.button(text="📈 Сделки", callback_data="journal:export_filter:trades")
    kb.button(text="⛔ Ошибки", callback_data="journal:export_filter:errors")
    kb.button(text="🧾 Без авто", callback_data="journal:export_filter:not_auto")
    kb.button(text="📒 Всё", callback_data="journal:export_filter:all")
    kb.button(text="⬅️ Назад", callback_data="journal:1")

    kb.adjust(2, 2, 1, 1)
    return kb.as_markup()


def build_export_format_keyboard(export_filter: str) -> InlineKeyboardMarkup:
    # Второй экран экспорта: после выбора фильтра выбираем формат.
    kb = InlineKeyboardBuilder()

    kb.button(text="📄 CSV", callback_data=f"journal:export_csv:{export_filter}")
    kb.button(text="📊 Excel", callback_data=f"journal:export_xlsx:{export_filter}")
    kb.button(text="⬅️ Назад", callback_data="journal:actions")

    kb.adjust(2, 1)
    return kb.as_markup()


def render_actions() -> str:
    return (
        "<b>📤 Экспорт</b>\n\n"
        "<b>СИСТЕМА · Журнал</b>\n\n"
        "Что экспортировать?"
    )


def render_export_format(export_filter: str) -> str:
    # Показываем выбранный фильтр перед выбором CSV/XLSX.
    label = journal_export_filter_label(export_filter)

    return (
        f"<b>📤 Экспорт · {label}</b>\n\n"
        "<b>СИСТЕМА · Журнал</b>\n\n"
        "Выберите формат:"
    )


def build_clear_confirm_keyboard() -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="Очистить всё", callback_data="journal:clear")
    kb.button(text="Старше 90 дней", callback_data="journal:clear_older:90")
    kb.button(text="⬅️ Назад", callback_data="settings:journal")
    kb.button(text="📒 Журнал", callback_data="journal:1")
    kb.adjust(2, 2)
    return kb.as_markup()


def _format_int(
    value: NumericLike | None,
    *,
    default: int = 0,
) -> int:
    number = safe_float(value)

    if number is None:
        return default

    return int(number)


def render_clear_confirm(
    *,
    total_count: NumericLike,
    deleted_count: NumericLike | None = None,
    no_old_records_days: NumericLike | None = None,
) -> str:
    total = _format_int(total_count)

    lines = [
        "<b>⚠️ Очистить журнал</b>",
        "",
        "<b>СИСТЕМА</b> · Настройки · Журнал",
        "",
        f"📄 Записей: {total}",
    ]

    if deleted_count is not None:
        lines.append(f"🧹 Удалено записей: {_format_int(deleted_count)}")

    if no_old_records_days is not None:
        lines.append(
            f"📭 Нет записей старше {_format_int(no_old_records_days)} дней"
        )

    return "\n".join(lines)


def _parse_local_datetime(value: object) -> datetime | None:
    try:
        settings = load_settings()
        raw_value = str(value or "")
        dt = datetime.fromisoformat(raw_value)

        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=ZoneInfo("UTC"))

        return dt.astimezone(ZoneInfo(settings.tz))
    except Exception:
        return None


def _date_group_label(dt: datetime | None) -> str:
    if dt is None:
        return "Без даты"

    settings = load_settings()
    today = datetime.now(ZoneInfo(settings.tz)).date()

    if dt.date() == today:
        return "Сегодня"

    if (today - dt.date()).days == 1:
        return "Вчера"

    return dt.strftime("%Y-%m-%d")


def _time_label(
    dt: datetime | None,
    raw_value: object,
) -> str:
    if dt is None:
        return str(raw_value or "")

    return dt.strftime("%H:%M:%S")


def _event_title(event_type: object) -> str:
    return event_title(str(event_type or ""))


def _humanize_message(message: object) -> str:
    text = str(message or "")
    lower = text.lower()

    for technical_text, human_text in TECH_TO_HUMAN_MESSAGES.items():
        if technical_text in lower:
            return human_text

    return text


def _payload(event: JsonDict) -> JsonDict:
    payload = event.get("payload")

    if isinstance(payload, dict):
        return payload

    return {}


def _render_auto_signal(
    event: JsonDict,
    created_time: str,
) -> list[str]:
    level = str(event.get("level") or "INFO").upper()
    icon = LEVEL_ICONS.get(level, "•")
    title = _html(_event_title(event.get("event_type")))
    message = _html(_humanize_message(event.get("message")))

    lines = [
        f"{icon} <b>{level}</b> · {title}",
        created_time,
    ]

    if message:
        lines.append(message)

    return lines


def _render_default_event(
    event: JsonDict,
    created_time: str,
) -> list[str]:
    level = str(event.get("level") or "INFO").upper()
    icon = LEVEL_ICONS.get(level, "•")
    title = _html(_event_title(event.get("event_type")))
    message = _html(_humanize_message(event.get("message")))

    lines = [
        f"{icon} <b>{level}</b> · {title}",
        created_time,
    ]

    if message:
        lines.append(message)

    return lines


def render(
    events: JsonList,
    page: NumericLike,
    total_pages: NumericLike,
) -> str:
    lines = [
        "<b>📒 Журнал</b>",
        "",
        "<b>СИСТЕМА</b>",
        "",
    ]

    if not events:
        lines.append("Событий пока нет.")
        return "\n".join(lines)

    current_group: str | None = None

    for raw_event in events:
        if not isinstance(raw_event, dict):
            continue

        event: JsonDict = raw_event

        raw_created_at = event.get("created_at") or ""
        dt = _parse_local_datetime(raw_created_at)
        group_label = _date_group_label(dt)
        created_time = _time_label(dt, raw_created_at)

        if group_label != current_group:
            current_group = group_label
            lines.append(f"<b>{group_label}</b>")
            lines.append("")

        event_type = str(event.get("event_type") or "")

        if event_type in {"signal_summary", "signal_ready"}:
            lines.extend(_render_auto_signal(event, created_time))
        else:
            lines.extend(_render_default_event(event, created_time))

        lines.append("")

    return "\n".join(lines).rstrip()