from __future__ import annotations

from aiogram.types import (
    InlineKeyboardButton,
    InlineKeyboardMarkup,
    ReplyKeyboardMarkup,
    KeyboardButton,
)
from aiogram.utils.keyboard import InlineKeyboardBuilder

from app import runtime
from app.common import money
from app.config import get_settings
from app.models import Category, Item


def main_menu(is_admin: bool = False) -> ReplyKeyboardMarkup:
    rows = [
        [KeyboardButton(text="◈ Catalog"), KeyboardButton(text="◇ Profile")],
        [KeyboardButton(text="➕ Add balance"), KeyboardButton(text="≡ My purchases")],
        [KeyboardButton(text="★ Leaderboard"), KeyboardButton(text="⋯ More")],
    ]
    rows.append([KeyboardButton(text="Create your own bot")])
    if is_admin:
        rows.append([KeyboardButton(text="◆ Admin panel")])
    return ReplyKeyboardMarkup(keyboard=rows, resize_keyboard=True)


def other_menu_kb(support: str, channel: str, reviews: str) -> InlineKeyboardMarkup:
    """Build the "More" menu from already-resolved https links ("" = hide the button).

    Link resolution (incl. invite links for private chats) happens in the handler,
    which has the bot instance — keyboards must stay free of API/`middlewares` imports.
    """
    kb = InlineKeyboardBuilder()
    kb.button(text="⬡ Google Authenticator (2FA)", callback_data="other:2fa")
    if support:
        kb.button(text="⊙ Support", url=support)
    if channel:
        kb.button(text="» Channel", url=channel)
    if reviews:
        kb.button(text="⭐ Reviews group", url=reviews)
    kb.adjust(1)
    return kb.as_markup()


def _chat_url(chat: str) -> str:
    """Best-effort clickable https link for a chat reference.

    Accepts a full http(s) URL, a `t.me/…` link, an @username, or a bare username;
    returns "" for anything that can't form a valid button URL (e.g. numeric -100… ids),
    so callers never build an invalid URL button (Telegram rejects those with a 400).
    """
    import re

    chat = (chat or "").strip()
    if not chat:
        return ""
    if chat.startswith("http://") or chat.startswith("https://"):
        return chat
    if chat.startswith("t.me/") or chat.startswith("telegram.me/"):
        return "https://" + chat
    if chat.startswith("@"):
        chat = chat[1:]
    if re.fullmatch(r"[A-Za-z][A-Za-z0-9_]{3,}", chat):
        return f"https://t.me/{chat}"
    # numeric -100… ids and anything else have no public link
    return ""


def twofa_input_kb() -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="← Back", callback_data="other:menu")
    return kb.as_markup()


def twofa_result_kb() -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="↻ Refresh code", callback_data="otpgen:ref")
    kb.button(text="← More", callback_data="other:menu")
    kb.adjust(1)
    return kb.as_markup()


def categories_kb(categories: list[Category]) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    for cat in categories:
        kb.button(text=f"▪ {cat.name}", callback_data=f"cat:{cat.id}")
    kb.adjust(1)
    return kb.as_markup()


def items_kb(
    items: list[Item], currency: str, in_stock_ids: set[int] | None = None
) -> InlineKeyboardMarkup:
    in_stock_ids = in_stock_ids or set()
    kb = InlineKeyboardBuilder()
    for item in items:
        # Colored inline buttons: green (success) in stock, red (danger) out of stock.
        style = "success" if item.id in in_stock_ids else "danger"
        kb.button(
            text=f"{item.name} — {money(item.price)} {currency}",
            callback_data=f"item:{item.id}",
            style=style,
        )
    kb.button(text="← Categories", callback_data="catalog")
    kb.adjust(1)
    return kb.as_markup()


def item_card_kb(
    item_id: int, category_id: int, in_stock: bool, review_count: int = 0
) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    if in_stock:
        kb.button(text="✓ Buy", callback_data=f"buy:{item_id}", style="success")
    if review_count:
        kb.button(text=f"★ Reviews ({review_count})", callback_data=f"revs:{item_id}")
    kb.button(text="← Back", callback_data=f"cat:{category_id}")
    kb.adjust(1)
    return kb.as_markup()


def item_reviews_back_kb(item_id: int) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="← Back", callback_data=f"item:{item_id}")
    return kb.as_markup()


def delivery_kb(purchase_id: int, has_2fa: bool) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    if has_2fa:
        kb.button(text="⬡ Get 2FA code", callback_data=f"otp:{purchase_id}")
    kb.button(text="★ Leave a review", callback_data=f"rev:{purchase_id}")
    kb.adjust(1)
    return kb.as_markup()


def review_rating_kb(purchase_id: int) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    for n in range(1, 6):
        kb.button(text=f"{n}★", callback_data=f"rate:{purchase_id}:{n}")
    kb.button(text="✕ Cancel", callback_data="rev:cancel")
    kb.adjust(5, 1)
    return kb.as_markup()


def otp_button_kb(purchase_id: int) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="⬡ Get 2FA code", callback_data=f"otp:{purchase_id}")
    return kb.as_markup()


def purchases_2fa_kb(rows: list[tuple[str, int]]) -> InlineKeyboardMarkup:
    """rows = [(item_name, purchase_id), ...] — items that have 2FA attached."""
    kb = InlineKeyboardBuilder()
    for name, pid in rows:
        kb.button(text=f"⬡ {name}", callback_data=f"otp:{pid}")
    kb.adjust(1)
    return kb.as_markup()


def crypto_invoice_kb(pay_url: str, topup_id: int) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="◆ Pay", url=pay_url)
    kb.button(text="✓ I paid — check", callback_data=f"checkpay:{topup_id}")
    kb.adjust(1)
    return kb.as_markup()


def topup_review_kb(topup_id: int) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="✓ Approve", callback_data=f"topok:{topup_id}")
    kb.button(text="✕ Decline", callback_data=f"topno:{topup_id}")
    kb.adjust(2)
    return kb.as_markup()


def admin_menu() -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="+ Category", callback_data="a:addcat")
    kb.button(text="× Categories", callback_data="a:delcat")
    kb.button(text="+ Item", callback_data="a:additem")
    kb.button(text="✎ Edit item", callback_data="a:edititem")
    kb.button(text="× Items", callback_data="a:delitem")
    kb.button(text="↑ Add stock", callback_data="a:addstock")
    kb.button(text="± Give balance", callback_data="a:givebal")
    kb.button(text="▦ Statistics", callback_data="a:stats")
    kb.button(text="➤ Broadcast", callback_data="a:cast")
    kb.button(text="🖼 Promo post", callback_data="a:promo")
    kb.button(text="⚙ Settings", callback_data="a:settings")
    kb.adjust(2)
    return kb.as_markup()


def _short(value: str, limit: int = 24) -> str:
    value = (value or "").strip()
    if not value:
        return "not set"
    return value if len(value) <= limit else value[: limit - 1] + "…"


def settings_menu_kb() -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(
        text=f"⊙ Support: {_short(runtime.get(runtime.SUPPORT_URL))}",
        callback_data="a:set:support",
    )
    kb.button(
        text=f"🔔 Required channel: {_short(runtime.get(runtime.REQUIRED_CHANNEL))}",
        callback_data="a:set:reqchan",
    )
    kb.button(
        text=f"⭐ Reviews group: {_short(runtime.get(runtime.REVIEWS_GROUP))}",
        callback_data="a:set:revgroup",
    )
    kb.button(
        text=f"🛍 Shop channel: {_short(runtime.get(runtime.SHOP_CHANNEL))}",
        callback_data="a:set:shopchan",
    )
    token_state = "set ✅" if runtime.get(runtime.CRYPTO_TOKEN) else "not set"
    kb.button(
        text=f"💳 CryptoBot token: {token_state}",
        callback_data="a:set:crypto",
    )
    kb.button(
        text=f"🪙 Crypto asset: {_short(runtime.crypto_asset())}",
        callback_data="a:set:asset",
    )
    kb.button(text="← Back", callback_data="a:menu")
    kb.adjust(1)
    return kb.as_markup()


def promo_confirm_kb(item_id: int) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="✓ Publish", callback_data=f"promo:pub:{item_id}")
    kb.button(text="✕ Cancel", callback_data="a:menu")
    kb.adjust(2)
    return kb.as_markup()


def buy_now_kb(deep_link: str) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="🛒 Buy now", url=deep_link)
    return kb.as_markup()


def sub_gate_kb(join_url: str) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    if join_url:
        kb.button(text="📢 Join the channel", url=join_url)
    kb.button(text="✓ I've subscribed", callback_data="subcheck")
    kb.adjust(1)
    return kb.as_markup()


def item_edit_menu_kb(item_id: int, is_active: bool) -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="✎ Name", callback_data=f"editf:name:{item_id}")
    kb.button(text="✎ Description", callback_data=f"editf:desc:{item_id}")
    kb.button(text="✎ Price", callback_data=f"editf:price:{item_id}")
    kb.button(
        text="🚫 Hide (deactivate)" if is_active else "✅ Show (activate)",
        callback_data=f"editf:toggle:{item_id}",
    )
    kb.button(text="← Items", callback_data="a:edititem")
    kb.adjust(1)
    return kb.as_markup()


def back_admin_kb() -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="← Admin panel", callback_data="a:menu")
    return kb.as_markup()


def pick_list_kb(rows: list[tuple[str, str]], back: str = "a:menu") -> InlineKeyboardMarkup:
    """rows = [(text, callback_data), ...]"""
    kb = InlineKeyboardBuilder()
    for text, cb in rows:
        kb.button(text=text, callback_data=cb)
    kb.button(text="← Back", callback_data=back)
    kb.adjust(1)
    return kb.as_markup()


def cancel_kb() -> InlineKeyboardMarkup:
    kb = InlineKeyboardBuilder()
    kb.button(text="✕ Cancel", callback_data="a:menu")
    return kb.as_markup()
