# -*- coding: utf-8 -*-
"""
نقطهٔ ورود cPanel (Setup Python App / Passenger).
  /                    ← مینی‌اپ
  /api/...             ← API مینی‌اپ
  /webhook/<secret>    ← دریافت آپدیت‌های تلگرام (وبهوک)
  /setup               ← صفحهٔ یک‌بارهٔ ثبت وبهوک (با توکن ربات)
  /cron/<secret>       ← اجرای کارهای دوره‌ای (اختیاری، جایگزین cron.py)
"""
import os
import sys
import json
import hmac
import hashlib
import time
import threading
import traceback
from concurrent.futures import ThreadPoolExecutor
from html import escape
from urllib.parse import parse_qs

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
if BASE_DIR not in sys.path:
    sys.path.insert(0, BASE_DIR)
os.chdir(BASE_DIR)

# خروجی UTF-8 (روی هاست‌ها گاهی locale ساده است و print فارسی خطا می‌دهد) + لاگ در فایل
try:
    os.makedirs(os.path.join(BASE_DIR, "logs"), exist_ok=True)
    _log = os.path.join(BASE_DIR, "logs", "app.log")
    if os.path.exists(_log) and os.path.getsize(_log) > 2 * 1024 * 1024:
        os.replace(_log, _log + ".old")
    _lf = open(_log, "a", buffering=1, encoding="utf-8", errors="replace")
    sys.stdout = _lf
    sys.stderr = _lf
except Exception:
    for _s in (sys.stdout, sys.stderr):
        try:
            _s.reconfigure(encoding="utf-8", errors="replace")
        except Exception:
            pass

import bot_core as core  # noqa: E402
from telebot import types as tg_types  # noqa: E402

_init_lock = threading.Lock()
_pool = ThreadPoolExecutor(max_workers=int(os.environ.get("BOT_THREADS") or 8))


def _secrets():
    tok = (core.TOKEN or "").encode()
    path_secret = hashlib.sha256(b"wh:" + tok).hexdigest()[:32]
    header_secret = hashlib.sha256(b"hdr:" + tok).hexdigest()[:32]
    return path_secret, header_secret


def _ensure_init():
    if core._app_ready:
        return
    with _init_lock:
        core.init_app()


def _handle_update(update):
    try:
        core.bot.process_new_updates([update])
    except Exception:
        traceback.print_exc()


def _resp(start_response, code, body, ctype="text/plain; charset=utf-8", extra=None):
    data = body if isinstance(body, bytes) else str(body).encode("utf-8")
    status = {200: "200 OK", 204: "204 No Content", 302: "302 Found", 400: "400 Bad Request",
              401: "401 Unauthorized", 403: "403 Forbidden", 404: "404 Not Found",
              500: "500 Internal Server Error"}.get(code, f"{code} Error")
    headers = [("Content-Type", ctype), ("Content-Length", str(len(data))),
               ("Cache-Control", "no-store"),
               ("Access-Control-Allow-Origin", "*"),
               ("Access-Control-Allow-Headers", "Content-Type, X-Telegram-Init-Data"),
               ("Access-Control-Allow-Methods", "GET, POST, OPTIONS")]
    if extra:
        headers += extra
    start_response(status, headers)
    return [data]


def _json(start_response, code, obj):
    return _resp(start_response, code, json.dumps(obj, ensure_ascii=False),
                 "application/json; charset=utf-8")


def _base_url(environ):
    host = environ.get("HTTP_X_FORWARDED_HOST") or environ.get("HTTP_HOST") or environ.get("SERVER_NAME", "")
    return "https://" + host.split(",")[0].strip() + (environ.get("SCRIPT_NAME") or "").rstrip("/")


def _api(environ, start_response, path, body):
    u = core.miniapp_auth(environ.get("HTTP_X_TELEGRAM_INIT_DATA", ""))
    if not u:
        return _json(start_response, 401, {"error": "این صفحه فقط از داخل تلگرام کار می‌کند."})
    try:
        if path == "/api/bootstrap":
            return _json(start_response, 200, core.miniapp_bootstrap(u))
        if path == "/api/buy":
            return _json(start_response, 200, core.miniapp_buy(u, body))
        if path == "/api/test":
            return _json(start_response, 200, core.miniapp_test(u))
        return _json(start_response, 404, {"error": "not found"})
    except core.MiniErr as e:
        return _json(start_response, e.code, {"error": str(e)})
    except Exception:
        traceback.print_exc()
        return _json(start_response, 500, {"error": "خطای داخلی سرور"})


_SETUP_FORM = """<!DOCTYPE html><html lang="fa" dir="rtl"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"><title>راه‌اندازی ربات</title>
<style>body{font-family:Tahoma,sans-serif;max-width:480px;margin:40px auto;padding:0 16px}
input,button{width:100%%;padding:12px;margin:8px 0;font-size:15px;box-sizing:border-box}
pre{background:#f3f3f3;padding:10px;overflow:auto;direction:ltr;text-align:left;white-space:pre-wrap}</style></head>
<body><h3>راه‌اندازی ربات روی هاست</h3>%s
<form method="post"><p>برای تأیید، توکن ربات را وارد کنید:</p>
<input type="password" name="token" placeholder="BOT_TOKEN" autocomplete="off">
<button type="submit">ثبت وبهوک و مینی‌اپ</button></form></body></html>"""


def _setup(environ, start_response, method):
    if method == "GET":
        return _resp(start_response, 200, _SETUP_FORM % "", "text/html; charset=utf-8")
    try:
        n = min(int(environ.get("CONTENT_LENGTH") or 0), 4096)
        form = parse_qs(environ["wsgi.input"].read(n).decode("utf-8", "replace"))
    except Exception:
        form = {}
    given = (form.get("token") or [""])[0].strip()
    if not core.TOKEN or not hmac.compare_digest(given.encode(), core.TOKEN.encode()):
        return _resp(start_response, 403, _SETUP_FORM % "<p style='color:red'>توکن نادرست است.</p>",
                     "text/html; charset=utf-8")
    base = _base_url(environ)
    path_secret, header_secret = _secrets()
    url = f"{base}/webhook/{path_secret}"
    try:
        _ensure_init()
        core.bot.set_webhook(url=url, secret_token=header_secret, max_connections=20,
                             allowed_updates=["message", "callback_query"])
        core.set_setting("miniapp_url", base + "/")
        info = core.bot.get_webhook_info()
        msg = (f"<p style='color:green'>✅ انجام شد.</p><p>وبهوک:</p><pre>{escape(url)}</pre>"
               f"<p>مینی‌اپ:</p><pre>{escape(base)}/</pre>"
               f"<p>خطای آخر تلگرام: <b>{escape(str(info.last_error_message or 'ندارد'))}</b></p>"
               f"<p>آدرس کرون (اختیاری):</p><pre>{escape(base)}/cron/{path_secret}</pre>")
    except Exception as e:
        traceback.print_exc()
        msg = f"<p style='color:red'>❌ خطا: {escape(str(e))}</p>"
    return _resp(start_response, 200, _SETUP_FORM % msg, "text/html; charset=utf-8")


def application(environ, start_response):
    method = environ.get("REQUEST_METHOD", "GET").upper()
    path = (environ.get("PATH_INFO") or "/").split("?", 1)[0]
    try:
        if method == "OPTIONS":
            return _resp(start_response, 204, "")

        if path == "/setup":
            return _setup(environ, start_response, method)

        _ensure_init()
        path_secret, header_secret = _secrets()

        if path == f"/webhook/{path_secret}" and method == "POST":
            got = environ.get("HTTP_X_TELEGRAM_BOT_API_SECRET_TOKEN", "")
            if not hmac.compare_digest(got, header_secret):
                return _resp(start_response, 403, "forbidden")
            n = int(environ.get("CONTENT_LENGTH") or 0)
            raw = environ["wsgi.input"].read(n).decode("utf-8")
            update = tg_types.Update.de_json(raw)
            # جلوگیری از پردازش دوباره (ری‌ترای تلگرام)
            if core.dbx("INSERT OR IGNORE INTO processed_updates (update_id, ts) VALUES (?, ?)",
                        (int(update.update_id), int(time.time()))) == 1:
                _pool.submit(_handle_update, update)
            return _resp(start_response, 200, "ok")

        if path == f"/cron/{path_secret}":
            threading.Thread(target=core.run_cron_tick, daemon=True).start()
            return _resp(start_response, 200, "started")

        if path in ("", "/", "/index.html"):
            if (environ.get("SCRIPT_NAME") and not environ.get("PATH_INFO")):
                return _resp(start_response, 302, "", extra=[("Location", environ["SCRIPT_NAME"] + "/")])
            return _resp(start_response, 200, core.MINIAPP_HTML, "text/html; charset=utf-8")

        if path == "/api/menu":
            return _json(start_response, 200, {"buttons": core.miniapp_menu_items()})

        if path.startswith("/api/"):
            body = {}
            if method == "POST":
                try:
                    n = min(int(environ.get("CONTENT_LENGTH") or 0), 65536)
                    body = json.loads(environ["wsgi.input"].read(n) or b"{}")
                    if not isinstance(body, dict):
                        body = {}
                except Exception:
                    body = {}
            return _api(environ, start_response, path, body)

        return _resp(start_response, 404, "not found")
    except Exception:
        traceback.print_exc()
        return _resp(start_response, 500, "internal error")
