# handlers/user.py
from __future__ import annotations
import html
import logging
from aiogram import Router, Bot
from aiogram.filters import CommandStart
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder

from config import ConferenceEvent, WebinarEvent, IdleEvent
from services.archive import (
    build_archive_keyboard,
    channel_url,
    format_material_response,
    resolve_welcome_text,
)
from db import record_start, record_subscription, record_material_event
from services.subscription import check_subscriptions, is_subscribed
from services.calendar import google_calendar_url, format_event_time

logger = logging.getLogger(__name__)
router = Router()

WEBINAR_DEFAULT_WELCOME_TEMPLATE = (
    "👋 <b>{title}</b>\n"
    "📅 {when}\n"
    "⏱ ~{duration} минут\n\n"
    "{description}"
)


async def _send_webinar_welcome(message: Message, event: WebinarEvent, welcome) -> None:
    url = google_calendar_url(event)
    builder = InlineKeyboardBuilder()
    builder.row(InlineKeyboardButton(text="📅 Добавить в Google Calendar", url=url))
    keyboard = builder.as_markup()

    if welcome and welcome.text:
        text = welcome.text
    else:
        text = WEBINAR_DEFAULT_WELCOME_TEMPLATE.format(
            title=event.title,
            when=format_event_time(event),
            duration=event.duration_minutes,
            description=event.description,
        )

    photo_file_id = welcome.photo_file_id if welcome else None
    if photo_file_id and len(text) <= 1024:
        await message.answer_photo(
            photo=photo_file_id,
            caption=text,
            reply_markup=keyboard,
            parse_mode="HTML",
        )
    elif photo_file_id:
        await message.answer_photo(photo=photo_file_id)
        await message.answer(text, reply_markup=keyboard, parse_mode="HTML", disable_web_page_preview=True)
    else:
        await message.answer(text, reply_markup=keyboard, parse_mode="HTML", disable_web_page_preview=True)


async def _send_conference_welcome(message: Message, event: ConferenceEvent, welcome) -> None:
    builder = InlineKeyboardBuilder()
    builder.row(InlineKeyboardButton(text="✅ Я подписался на всех", callback_data="check_subscriptions"))
    keyboard = builder.as_markup()

    if welcome and welcome.photo_file_id:
        await message.answer_photo(
            photo=welcome.photo_file_id,
            caption=welcome.text,
            reply_markup=keyboard,
            parse_mode="HTML",
        )
    else:
        speakers_list = "\n".join(
            f"• <a href='https://t.me/{s.channel.lstrip('@')}'>{html.escape(s.name)}</a>"
            for s in event.speakers
        )
        text = (welcome.text if welcome else None) or (
            f"👋 Добро пожаловать на <b>{event.title}</b>!\n\n"
            f"Подпишись на каналы всех спикеров:\n{speakers_list}\n\n"
            f"Затем нажми кнопку ниже."
        )
        await message.answer(text, reply_markup=keyboard, parse_mode="HTML", disable_web_page_preview=True)


async def _send_archive_welcome(message: Message, archive) -> None:
    text = resolve_welcome_text(archive)
    keyboard = build_archive_keyboard(archive.materials)
    photo_file_id = archive.welcome.photo_file_id if archive.welcome else None
    if photo_file_id and len(text) <= 1024:
        await message.answer_photo(
            photo=photo_file_id,
            caption=text,
            reply_markup=keyboard,
            parse_mode="HTML",
        )
    elif photo_file_id:
        await message.answer_photo(photo=photo_file_id)
        await message.answer(text, reply_markup=keyboard, parse_mode="HTML", disable_web_page_preview=True)
    else:
        await message.answer(text, reply_markup=keyboard, parse_mode="HTML", disable_web_page_preview=True)


def setup_user_handlers(db, event_holder: dict, welcome_holder: dict, archive_holder: dict) -> None:
    @router.message(CommandStart())
    async def cmd_start(message: Message) -> None:
        if message.from_user is None:
            return
        event = event_holder["event"]
        await record_start(db, user_id=message.from_user.id, conference_id=event.event_id)

        if event.type == "webinar":
            await _send_webinar_welcome(message, event, welcome_holder.get("welcome"))
        elif event.type == "idle":
            await _send_archive_welcome(message, archive_holder["archive"])
        else:
            await _send_conference_welcome(message, event, welcome_holder.get("welcome"))

    @router.callback_query(lambda c: c.data == "check_subscriptions")
    async def on_check_subscriptions(callback: CallbackQuery, bot: Bot) -> None:
        await callback.answer()
        event = event_holder["event"]
        if event.type != "conference":
            await callback.message.answer("Событие сменилось, нажми /start.")
            return
        not_subscribed = await check_subscriptions(bot, callback.from_user.id, event.speakers)
        if not not_subscribed:
            try:
                await record_subscription(db, user_id=callback.from_user.id, conference_id=event.event_id)
            except ValueError:
                await callback.message.answer("Что-то пошло не так. Отправь /start и попробуй снова.")
                return
            await callback.message.answer(
                "🎉 Отлично! Ты подписан на всех спикеров.\n"
                "Ссылка на трансляцию придёт в этот бот за день до конференции."
            )
        else:
            speakers_map = {s.channel: s.name for s in event.speakers}
            missing = "\n".join(
                f"• <a href='https://t.me/{ch.lstrip('@')}'>{html.escape(speakers_map.get(ch, ch))}</a>"
                for ch in not_subscribed
            )
            builder = InlineKeyboardBuilder()
            builder.row(InlineKeyboardButton(
                text="✅ Я подписался на всех",
                callback_data="check_subscriptions",
            ))
            await callback.message.answer(
                f"Не хватает подписок:\n{missing}\n\nПодпишись и нажми кнопку снова.",
                reply_markup=builder.as_markup(),
                parse_mode="HTML",
                disable_web_page_preview=True,
            )

    @router.callback_query(lambda c: c.data is not None and c.data.startswith("material:"))
    async def on_material_click(callback: CallbackQuery) -> None:
        await callback.answer()
        event = event_holder["event"]
        if event.type != "idle":
            await callback.message.answer("Архив больше не активен. Нажми /start.")
            return
        material_id = callback.data.split(":", 1)[1]
        archive = archive_holder["archive"]
        target = next((m for m in archive.materials if m.id == material_id), None)
        if target is None:
            await callback.message.answer("Этот материал больше недоступен. Нажми /start, чтобы увидеть актуальный список.")
            return
        gated = bool(archive.required_channel)
        await _record_material(event.event_id, callback.from_user.id, target.id, "click")
        text, keyboard = format_material_response(target, gated)
        await callback.message.answer(text, reply_markup=keyboard, parse_mode="HTML", disable_web_page_preview=True)

    async def _record_material(conference_id: str, user_id: int, material_id: str, action: str) -> None:
        # Fail-open: analytics must never break the user-facing flow.
        try:
            await record_material_event(db, user_id, conference_id, material_id, action)
        except Exception as e:
            logger.warning("material event (%s) not recorded: %s", action, e)

    async def _deliver_material(message: Message, material) -> None:
        builder = InlineKeyboardBuilder()
        builder.row(InlineKeyboardButton(text=material.button_label, url=material.url))
        await message.answer("Готово 👇", reply_markup=builder.as_markup(), disable_web_page_preview=True)

    @router.callback_query(lambda c: c.data is not None and c.data.startswith("watch:"))
    async def on_watch(callback: CallbackQuery, bot: Bot) -> None:
        await callback.answer()
        event = event_holder["event"]
        if event.type != "idle":
            await callback.message.answer("Архив больше не активен. Нажми /start.")
            return
        material_id = callback.data.split(":", 1)[1]
        archive = archive_holder["archive"]
        target = next((m for m in archive.materials if m.id == material_id), None)
        if target is None:
            await callback.message.answer("Материал больше недоступен. Нажми /start.")
            return

        channel = archive.required_channel
        if not channel:
            # Gate was turned off while the button was live — deliver immediately.
            await _record_material(event.event_id, callback.from_user.id, target.id, "delivered")
            await _deliver_material(callback.message, target)
            return

        status = await is_subscribed(bot, callback.from_user.id, channel)
        if status is True:
            await _record_material(event.event_id, callback.from_user.id, target.id, "delivered")
            await _deliver_material(callback.message, target)
        elif status is False:
            await _record_material(event.event_id, callback.from_user.id, target.id, "gate_shown")
            builder = InlineKeyboardBuilder()
            builder.row(InlineKeyboardButton(text="📢 Подписаться", url=channel_url(channel)))
            builder.row(InlineKeyboardButton(text="✅ Я подписался", callback_data=f"watch:{target.id}"))
            await callback.message.answer(
                f"Чтобы открыть материал, подпишись на {channel} и нажми «✅ Я подписался».",
                reply_markup=builder.as_markup(),
                disable_web_page_preview=True,
            )
        else:  # None — couldn't verify
            builder = InlineKeyboardBuilder()
            builder.row(InlineKeyboardButton(text="✅ Попробовать снова", callback_data=f"watch:{target.id}"))
            await callback.message.answer(
                "Не получилось проверить подписку, попробуй ещё раз через минуту.",
                reply_markup=builder.as_markup(),
            )

    @router.callback_query(lambda c: c.data is not None and c.data.startswith("check_sub:"))
    async def on_check_sub_with_conf(callback: CallbackQuery, bot: Bot) -> None:
        await callback.answer()
        conference_id = callback.data.split(":", 1)[1]
        if conference_id != event_holder["event"].event_id:
            await callback.message.answer("Конференция обновилась. Отправь /start снова.")
            return
        not_subscribed = await check_subscriptions(bot, callback.from_user.id, event_holder["event"].speakers)
        if not not_subscribed:
            try:
                await record_subscription(db, user_id=callback.from_user.id, conference_id=conference_id)
            except ValueError:
                await callback.message.answer("Что-то пошло не так. Отправь /start и попробуй снова.")
                return
            await callback.message.answer(
                "🎉 Отлично! Ты подписан на всех спикеров.\n"
                "Ссылка на трансляцию придёт в этот бот за день до конференции."
            )
        else:
            speakers_map = {s.channel: s.name for s in event_holder["event"].speakers}
            missing = "\n".join(
                f"• <a href='https://t.me/{ch.lstrip('@')}'>{html.escape(speakers_map.get(ch, ch))}</a>"
                for ch in not_subscribed
            )
            builder = InlineKeyboardBuilder()
            builder.row(InlineKeyboardButton(
                text="✅ Я подписался на всех",
                callback_data=f"check_sub:{conference_id}",
            ))
            await callback.message.answer(
                f"Не хватает подписок:\n{missing}\n\nПодпишись и нажми кнопку снова.",
                reply_markup=builder.as_markup(),
                parse_mode="HTML",
                disable_web_page_preview=True,
            )
