from __future__ import annotations

from aiogram import F, Router
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message

from app import runtime
from app.common import is_admin
from app.config import get_settings
from app.keyboards import _chat_url, main_menu, other_menu_kb, twofa_input_kb, twofa_result_kb
from app.middlewares import gate_markup, is_subscribed, resolve_join_url
from app.services import totp
from app.states import OtherStates

router = Router()

_OTHER_TEXT = "🔧 <b>More</b>\n\nExtra tools:"
_ASK_SECRET = (
    "🔐 <b>Google Authenticator</b>\n\n"
    "Send your 2FA secret (a key like <code>JBSW Y3DP EHPK 3PXP</code>).\n"
    "Spaces and case don't matter. You can also paste an <code>otpauth://</code> link.\n\n"
    "The bot will generate the current code, just like the Google Authenticator app."
)


def _format_code(secret: str) -> str:
    code, remaining = totp.generate(secret)
    pretty = f"{code[:3]} {code[3:]}" if len(code) == 6 else code
    bar = "🟩" * remaining + "⬜" * (30 - remaining)
    return (
        "🔐 <b>Google Authenticator</b>\n\n"
        f"Code: <code>{pretty}</code>\n"
        f"⏳ Valid for <b>{remaining}</b> more sec.\n"
        f"{bar}\n\n"
        "Tap “🔄 Refresh code” to get a fresh code."
    )


@router.callback_query(F.data == "subcheck")
async def subscription_check(callback: CallbackQuery) -> None:
    channel = runtime.get(runtime.REQUIRED_CHANNEL)
    if channel and not is_admin(callback.from_user.id) and not await is_subscribed(
        callback.bot, channel, callback.from_user.id
    ):
        await callback.answer("Still not subscribed 😔", show_alert=True)
        try:
            await callback.message.edit_reply_markup(
                reply_markup=await gate_markup(callback.bot, channel)
            )
        except Exception:
            pass
        return
    try:
        await callback.message.delete()
    except Exception:
        pass
    await callback.message.answer(
        "✅ Thanks for subscribing! You're all set.",
        reply_markup=main_menu(is_admin(callback.from_user.id)),
    )
    await callback.answer()


async def _other_markup(bot):
    """Resolve support/channel/reviews links (invite links for private chats) and build."""
    settings = get_settings()
    support = _chat_url(runtime.support_url())
    channel = await resolve_join_url(
        bot, runtime.get(runtime.SHOP_CHANNEL) or settings.channel_url
    )
    reviews = await resolve_join_url(bot, runtime.get(runtime.REVIEWS_GROUP))
    return other_menu_kb(support, channel, reviews)


@router.message(F.text == "⋯ More")
async def other_open(message: Message, state: FSMContext) -> None:
    await state.clear()
    await message.answer(_OTHER_TEXT, reply_markup=await _other_markup(message.bot))


@router.callback_query(F.data == "other:menu")
async def other_menu_cb(callback: CallbackQuery, state: FSMContext) -> None:
    await state.clear()
    await callback.message.edit_text(
        _OTHER_TEXT, reply_markup=await _other_markup(callback.bot)
    )
    await callback.answer()


@router.callback_query(F.data == "other:2fa")
async def twofa_start(callback: CallbackQuery, state: FSMContext) -> None:
    await state.set_state(OtherStates.twofa)
    await callback.message.edit_text(_ASK_SECRET, reply_markup=twofa_input_kb())
    await callback.answer()


@router.message(OtherStates.twofa)
async def twofa_input(message: Message, state: FSMContext) -> None:
    secret = totp.normalize_secret(message.text or "")
    if not totp.is_valid_secret(secret):
        await message.answer(
            "❌ That doesn't look like a valid 2FA secret. Try again.",
            reply_markup=twofa_input_kb(),
        )
        return
    await state.update_data(twofa_secret=secret)
    await message.answer(_format_code(secret), reply_markup=twofa_result_kb())


@router.callback_query(OtherStates.twofa, F.data == "otpgen:ref")
async def twofa_refresh(callback: CallbackQuery, state: FSMContext) -> None:
    data = await state.get_data()
    secret = data.get("twofa_secret", "")
    if not secret:
        await callback.answer("Secret not found, enter it again", show_alert=True)
        return
    try:
        text = _format_code(secret)
    except Exception:
        await callback.answer("❌ Generation error", show_alert=True)
        return
    try:
        await callback.message.edit_text(text, reply_markup=twofa_result_kb())
    except Exception:
        # text unchanged (same code) — Telegram returns an error, that's fine
        pass
    await callback.answer("Refreshed 🔄")
