"""
جریان مرحله‌ای + کیبوردهای اینلاین برای ربات تلگرام.
callback_data با پیشوند w. و حداکثر طول کوتاه (محدودیت تلگرام ۶۴ بایت).
"""

from __future__ import annotations

import html
import re
from typing import Any

from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.constants import ParseMode

# --- state در context.user_data ---
K_STEP = "wf_step"  # main | total | sym | confirm
K_SIDE = "wf_side"  # buy | sell
K_TOTAL = "wf_total"
K_SYMBOL = "wf_symbol"
K_AWAIT = "wf_await"  # total | sym | None
# nonce یک‌بارمصرف برای دکمهٔ «تأیید و اجرا» (جلوگیری از replay / کلیک روی پیام قدیمی)
K_GO_NONCE = "wf_go_nonce"

# ----------------------------------------------------------------
#  سیستم بصری یکدست — برند، جداکننده، سرتیتر، نشانگر گام
# ----------------------------------------------------------------
TITLE = "✦ <b>Metalot</b>  ·  <i>تقسیم حجم MT5</i>"
SEP = "\n────────────────\n"
HAIR = "\n· · · · · · · · · · · ·\n"  # جداکنندهٔ نرم‌تر برای داخل بخش‌ها


def step_pill(n: int, total_steps: int = 3) -> str:
    """نشانگر گام به سبک '●●○ گام ۲ از ۳'."""
    n = max(1, min(total_steps, int(n)))
    fa_digits = str.maketrans("0123456789", "۰۱۲۳۴۵۶۷۸۹")
    pills = "●" * n + "○" * (total_steps - n)
    return f"<code>{pills}</code>  ·  <b>گام {str(n).translate(fa_digits)} از {str(total_steps).translate(fa_digits)}</b>"


def fmt_lot(v: float) -> str:
    """نمایش تمیز لات (بدون .0 اضافه برای اعداد صحیح)."""
    return f"{float(v):g}"


def reset_flow(ud: dict[str, Any]) -> None:
    for k in (K_STEP, K_SIDE, K_TOTAL, K_SYMBOL, K_AWAIT, K_GO_NONCE):
        ud.pop(k, None)


def approx_orders(total: float, max_per: float) -> int:
    if max_per <= 0:
        return 0
    return int((total + max_per - 1e-9) // max_per)


def _side_pair(side: str) -> tuple[str, str]:
    """(emoji, persian_label) — مرکز یکتای نگاشت جهت."""
    if side == "buy":
        return "🟢", "خرید"
    return "🔴", "فروش"


def summary_lines(side: str, total: float, max_per: float, symbol: str) -> str:
    emoji, side_fa = _side_pair(side)
    n = approx_orders(total, max_per)
    t = html.escape(fmt_lot(total))
    m = html.escape(fmt_lot(max_per))
    sym = html.escape(symbol)
    return (
        f"{emoji}  <b>جهت</b>  ·  {side_fa}\n"
        f"📦  <b>حجم کل</b>  ·  <code>{t}</code> لات\n"
        f"✂️  <b>سقف هر سفارش</b>  ·  <code>{m}</code> لات\n"
        f"🏷  <b>نماد</b>  ·  <code>{sym}</code>\n"
        f"⚡  <b>تعداد سفارش تقریبی</b>  ·  <code>{n}</code>"
    )


def kb_main() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(
        [
            [
                InlineKeyboardButton("🟢  خرید مارکت", callback_data="w.s.b"),
                InlineKeyboardButton("🔴  فروش مارکت", callback_data="w.s.s"),
            ],
            [
                InlineKeyboardButton("📡  وضعیت MT5", callback_data="w.st"),
                InlineKeyboardButton("👁  حجم باز", callback_data="w.ex"),
            ],
            [InlineKeyboardButton("📖  راهنما", callback_data="w.h")],
            [InlineKeyboardButton("⚙️  تنظیمات", callback_data="w.cfg")],
        ]
    )


def kb_success_bar(symbol: str, magic: int | None = None) -> InlineKeyboardMarkup:
    sym = symbol[:32]
    refresh_data = f"w.pl|{sym}|{int(magic)}" if magic is not None else f"w.pl|{sym}"
    return InlineKeyboardMarkup(
        [
            [InlineKeyboardButton("🔄  بروزرسانی سود/ضرر", callback_data=refresh_data)],
            [InlineKeyboardButton("🏠  منو", callback_data="w.mn")],
        ]
    )


def kb_exposure_panel() -> InlineKeyboardMarkup:
    """پنل حجم باز — فقط بروزرسانی و منو؛ بدون معامله."""
    return InlineKeyboardMarkup(
        [
            [InlineKeyboardButton("🔄  بروزرسانی", callback_data="w.ex")],
            [InlineKeyboardButton("🏠  منو", callback_data="w.m")],
        ]
    )


def kb_totals() -> InlineKeyboardMarkup:
    presets = ["10", "25", "50", "100", "250", "500"]
    row1 = [InlineKeyboardButton(p, callback_data=f"w.t.{p}") for p in presets[:3]]
    row2 = [InlineKeyboardButton(p, callback_data=f"w.t.{p}") for p in presets[3:]]
    return InlineKeyboardMarkup(
        [
            row1,
            row2,
            [InlineKeyboardButton("✏️  حجم دلخواه", callback_data="w.t.c")],
            [InlineKeyboardButton("🏠  منو", callback_data="w.m")],
        ]
    )


def kb_symbol(default_symbol: str) -> InlineKeyboardMarkup:
    sym_short = default_symbol[:16]
    return InlineKeyboardMarkup(
        [
            [
                InlineKeyboardButton(
                    f"📌  {sym_short}  ·  پیش‌فرض",
                    callback_data="w.y.d",
                )
            ],
            [InlineKeyboardButton("✏️  نماد دیگر", callback_data="w.y.c")],
            [InlineKeyboardButton("⬅️  بازگشت", callback_data="w.b.t")],
        ]
    )


def kb_confirm(nonce: str) -> InlineKeyboardMarkup:
    """callback_data: w.go|{nonce} — باید با user_data[K_GO_NONCE] یکی باشد."""
    n = (nonce or "").strip()
    if len(n) > 20:
        n = n[:20]
    data = f"w.go|{n}" if n else "w.go|"
    return InlineKeyboardMarkup(
        [
            [InlineKeyboardButton("🚀  تأیید و اجرا", callback_data=data)],
            [
                InlineKeyboardButton("⬅️  بازگشت", callback_data="w.b.y"),
                InlineKeyboardButton("🔄  از اول", callback_data="w.rs"),
            ],
            [InlineKeyboardButton("❌  لغو", callback_data="w.m")],
        ]
    )


def main_panel_html(default_symbol: str, default_max: float) -> str:
    return (
        f"{TITLE}{SEP}"
        f"📌  نماد پیش‌فرض  ·  <code>{html.escape(default_symbol)}</code>\n"
        f"✂️  سقف هر سفارش  ·  <code>{default_max:g}</code> لات\n\n"
        f"<i>یک گزینه را از پایین انتخاب کن.</i>"
    )


def pick_total_html(side: str) -> str:
    emoji, side_fa = _side_pair(side)
    return (
        f"{step_pill(1)}\n"
        f"{emoji}  <b>{side_fa}</b>  ·  انتخاب حجم کل\n\n"
        f"یک پریست بزن، یا <b>حجم دلخواه</b> را بفرست."
    )


def pick_symbol_html(
    side: str,
    total: float,
    max_per: float,
    account_strip: str = "",
) -> str:
    emoji, side_fa = _side_pair(side)
    t = html.escape(fmt_lot(total))
    m = html.escape(fmt_lot(max_per))
    block = (
        f"{step_pill(2)}\n"
        f"{emoji}  <b>{side_fa}</b>  ·  انتخاب نماد\n\n"
        f"📦  حجم کل  ·  <code>{t}</code> لات\n"
        f"✂️  سقف هر سفارش  ·  <code>{m}</code> لات"
    )
    if account_strip.strip():
        block += f"{HAIR}{account_strip.strip()}"
    block += "\n\n<i>نماد را انتخاب کن.</i>"
    return block


def confirm_html(
    side: str,
    total: float,
    max_per: float,
    symbol: str,
    account_block: str = "",
) -> str:
    body = summary_lines(side, total, max_per, symbol)
    out = f"{step_pill(3)}\n✅  <b>تأیید نهایی</b>\n\n{body}"
    if account_block.strip():
        out += f"{HAIR}💹  <b>حساب و مارجین</b>  <i>(تقریبی)</i>\n{account_block.strip()}"
    return out


HELP_HTML = (
    f"{TITLE}{SEP}"
    f"📖  <b>راهنما</b>\n\n"
    f"<b>جریان کاری</b>\n"
    f"🟢/🔴  جهت  →  📦  حجم کل  →  🏷  نماد  →  ✅  تأیید\n"
    f"{HAIR}"
    f"<b>چه چیزهایی روی صفحه‌ی تأیید می‌بینی؟</b>\n"
    f"·  موجودی، اکویتی، مارجین آزاد\n"
    f"·  مارجین لازم تخمینی برای کل حجم\n"
    f"·  <b>سطح مارجین تخمینی</b> بعد از باز شدن همه‌ی سفارش‌ها\n"
    f"{HAIR}"
    f"<b>کنترل ریسک</b>\n"
    f"⚙️  سقف لات و سقف ریسک در منوی <b>تنظیمات</b> (ذخیره روی ربات).\n"
    f"🛡️  محدودیت‌ها از <b>خطای دستی</b> جلوگیری می‌کنند ‒ بازار را پیش‌بینی نمی‌کنند.\n"
    f"{HAIR}"
    f"<b>مشاهدهٔ وضعیت</b>\n"
    f"👁  <b>حجم باز</b>  ·  لات‌های باز کل نماد و ربات + سود/ضرر کل اکانت.\n"
    f"📡  <b>وضعیت MT5</b>  ·  اتصال و حساب.\n"
    f"{HAIR}"
    f"<b>بستن پوزیشن</b>\n"
    f"🔒  از خود <b>MT5</b> ببند — این ربات فقط باز کردن و مشاهدهٔ وضعیت دارد.\n"
    f"{HAIR}"
    f"<b>پیش‌نیاز</b>\n"
    f"💻  ترمینال MT5 باز + Algo Trading روشن.\n"
    f"📈  حجم بزرگ؟ <code>RISK_MAX_TOTAL_LOTS</code> را در تنظیمات بالا ببر."
)


async def send_main_menu_message(update: Update, default_symbol: str, default_max: float) -> None:
    text = main_panel_html(default_symbol, default_max)
    if update.message:
        await update.message.reply_text(
            text,
            reply_markup=kb_main(),
            parse_mode=ParseMode.HTML,
        )
    elif update.callback_query and update.callback_query.message:
        await update.callback_query.message.reply_text(
            text,
            reply_markup=kb_main(),
            parse_mode=ParseMode.HTML,
        )


async def edit_or_reply(query, text: str, reply_markup: InlineKeyboardMarkup | None) -> None:
    try:
        await query.edit_message_text(
            text=text,
            reply_markup=reply_markup,
            parse_mode=ParseMode.HTML,
        )
    except Exception:
        await query.message.reply_text(
            text=text,
            reply_markup=reply_markup,
            parse_mode=ParseMode.HTML,
        )


def parse_symbol_text(raw: str) -> str | None:
    """نماد MT5 را بدون تغییر حروف برمی‌گرداند (پسوند مثل `.p` نسبت به حروف حساس است)."""
    s = raw.strip()
    if not re.match(r"^[A-Za-z0-9._#+\-]{2,32}$", s):
        return None
    return s


# نگاشت ارقام و جداکننده‌های فارسی/عربی به ASCII
_FA_AR_DIGIT_MAP = str.maketrans(
    {
        # ارقام فارسی ۰..۹
        "\u06f0": "0", "\u06f1": "1", "\u06f2": "2", "\u06f3": "3", "\u06f4": "4",
        "\u06f5": "5", "\u06f6": "6", "\u06f7": "7", "\u06f8": "8", "\u06f9": "9",
        # ارقام عربی-هندی ٠..٩
        "\u0660": "0", "\u0661": "1", "\u0662": "2", "\u0663": "3", "\u0664": "4",
        "\u0665": "5", "\u0666": "6", "\u0667": "7", "\u0668": "8", "\u0669": "9",
        # جداکننده‌ها
        "\u066b": ".",  # ARABIC DECIMAL SEPARATOR (٫)
        "\u066c": "",   # ARABIC THOUSANDS SEPARATOR (٬) → حذف
        ",": ".",
        "\u060c": "",   # ARABIC COMMA (،) → حذف
        " ": "",        # فاصله‌های نادر
        "\u200c": "",   # ZWNJ
        "\u200f": "",   # RTL mark
        "\u200e": "",   # LTR mark
    }
)


def _normalize_numeric(text: str) -> str:
    return text.strip().translate(_FA_AR_DIGIT_MAP)


def parse_float_fa_en(text: str) -> float | None:
    t = _normalize_numeric(text)
    try:
        v = float(t)
    except ValueError:
        return None
    if not (v == v) or v in (float("inf"), float("-inf")):  # NaN / Inf
        return None
    if v <= 0:
        return None
    return v


def parse_nonneg_float(text: str) -> float | None:
    """برای سقف ریسک؛ صفر مجاز است."""
    t = _normalize_numeric(text)
    try:
        v = float(t)
    except ValueError:
        return None
    if not (v == v) or v in (float("inf"), float("-inf")):
        return None
    if v < 0:
        return None
    return v


def parse_int_fa_en(text: str) -> int | None:
    """تبدیل عدد صحیح فارسی/عربی/ASCII."""
    t = _normalize_numeric(text)
    if not t:
        return None
    try:
        return int(t)
    except ValueError:
        try:
            f = float(t)
        except ValueError:
            return None
        if float(int(f)) != f:
            return None
        return int(f)


def settings_panel_html(
    symbol: str,
    max_lot: float,
    magic: int,
    deviation: int,
    comment: str,
    risk_max: float,
    risk_orders: int,
    buf: float,
    minfeq: float,
) -> str:
    risk_line = "♾️ بدون سقف" if risk_max <= 0 else f"<code>{risk_max:g}</code> لات"
    cm = html.escape((comment or "")[:28])
    if len(comment or "") > 28:
        cm += "…"
    return (
        f"{TITLE}{SEP}"
        f"⚙️  <b>تنظیمات</b>  <i>· ذخیره برای این چت</i>\n"
        f"{HAIR}"
        f"🤖  <b>اکسپرت / معامله</b>\n"
        f"├ 📌  نماد  ·  <code>{html.escape(symbol)}</code>\n"
        f"├ ✂️  سقف هر سفارش  ·  <code>{max_lot:g}</code> لات\n"
        f"├ ✨  Magic  ·  <code>{magic}</code>\n"
        f"├ ↔️  انحراف  ·  <code>{deviation}</code> پوینت\n"
        f"└ 💬  کامنت  ·  <code>{cm or '—'}</code>\n"
        f"{HAIR}"
        f"🛡️  <b>ریسک</b>\n"
        f"├ 📏  سقف حجم کل  ·  {risk_line}\n"
        f"├ 🔢  حداکثر سفارش  ·  <code>{risk_orders}</code>\n"
        f"├ 📊  بافر مارجین  ·  <code>{buf:g}</code>٪\n"
        f"└ 💰  حداقل آزاد ٪ اکویتی  ·  <code>{minfeq:g}</code>٪\n"
        f"{HAIR}"
        f"<i>🔗 Magic و سقف لات را در اکسپرت MT5 هم همین مقادیر بگذار.</i>"
    )


def kb_settings_root() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(
        [
            [InlineKeyboardButton("📌  نماد پیش‌فرض", callback_data="w.cfg.sym")],
            [InlineKeyboardButton("✂️  سقف هر سفارش", callback_data="w.cfg.mx")],
            [
                InlineKeyboardButton("✨  Magic", callback_data="w.cfg.mg"),
                InlineKeyboardButton("↔️  انحراف", callback_data="w.cfg.dv"),
            ],
            [InlineKeyboardButton("💬  کامنت سفارش", callback_data="w.cfg.cm")],
            [InlineKeyboardButton("📏  سقف حجم کل (ریسک)", callback_data="w.cfg.rk")],
            [InlineKeyboardButton("🛠  ریسک پیشرفته", callback_data="w.cfg.adv")],
            [
                InlineKeyboardButton("♻️  بازنشانی به .env", callback_data="w.cfg.rst"),
                InlineKeyboardButton("🏠  منو", callback_data="w.m"),
            ],
        ]
    )


def kb_settings_max_menu() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(
        [
            [
                InlineKeyboardButton("۱", callback_data="w.cfg.mx|1"),
                InlineKeyboardButton("۲", callback_data="w.cfg.mx|2"),
                InlineKeyboardButton("۵", callback_data="w.cfg.mx|5"),
            ],
            [
                InlineKeyboardButton("۱۰", callback_data="w.cfg.mx|10"),
                InlineKeyboardButton("۲۰", callback_data="w.cfg.mx|20"),
                InlineKeyboardButton("۵۰", callback_data="w.cfg.mx|50"),
            ],
            [InlineKeyboardButton("✏️  عدد دلخواه", callback_data="w.cfg.mx|c")],
            [InlineKeyboardButton("⬅️  تنظیمات", callback_data="w.cfg")],
        ]
    )


def kb_settings_risk_menu() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(
        [
            [
                InlineKeyboardButton("۱۰۰", callback_data="w.cfg.rk|100"),
                InlineKeyboardButton("۲۵۰", callback_data="w.cfg.rk|250"),
                InlineKeyboardButton("۵۰۰", callback_data="w.cfg.rk|500"),
            ],
            [
                InlineKeyboardButton("۱۰۰۰", callback_data="w.cfg.rk|1000"),
                InlineKeyboardButton("۲۰۰۰", callback_data="w.cfg.rk|2000"),
            ],
            [
                InlineKeyboardButton("♾️  بدون سقف", callback_data="w.cfg.rk|0"),
                InlineKeyboardButton("✏️  عدد دلخواه", callback_data="w.cfg.rk|c"),
            ],
            [InlineKeyboardButton("⬅️  تنظیمات", callback_data="w.cfg")],
        ]
    )


def kb_settings_adv_menu() -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup(
        [
            [
                InlineKeyboardButton("📎  حداکثر ۵۰۰", callback_data="w.cfg.ord|500"),
                InlineKeyboardButton("۱۰۰۰", callback_data="w.cfg.ord|1000"),
                InlineKeyboardButton("۲۰۰۰", callback_data="w.cfg.ord|2000"),
            ],
            [
                InlineKeyboardButton("📊  بافر ۵٪", callback_data="w.cfg.buf|5"),
                InlineKeyboardButton("۱۰٪", callback_data="w.cfg.buf|10"),
                InlineKeyboardButton("۱۵٪", callback_data="w.cfg.buf|15"),
            ],
            [
                InlineKeyboardButton("💧  آزاد ۰٪", callback_data="w.cfg.mnf|0"),
                InlineKeyboardButton("۳٪", callback_data="w.cfg.mnf|3"),
                InlineKeyboardButton("۵٪", callback_data="w.cfg.mnf|5"),
                InlineKeyboardButton("۱۰٪", callback_data="w.cfg.mnf|10"),
            ],
            [InlineKeyboardButton("⬅️  تنظیمات", callback_data="w.cfg")],
        ]
    )
