import asyncio
import json
from pathlib import Path

from aiohttp import web
from aiogram import Bot, Dispatcher

from config import load_config
from database import create_pg_pool, create_redis_pool
from handlers.start import router as start_router, delete_expired_promos, main_menu_kb
from handlers.profile import router as profile_router
from utils.cryptopay import verify_webhook_signature


async def promo_cleanup_scheduler(db_pool):
    while True:
        await delete_expired_promos(db_pool)
        await asyncio.sleep(600)  # каждые 10 минут


async def _credit_payment(db_pool, uuid: str) -> tuple[int, float] | None:
    """Atomically set is_paid=true and return (user_id, amount) or None if already processed."""
    async with db_pool.acquire() as conn:
        row = await conn.fetchrow(
            "UPDATE payments SET is_paid=true WHERE uuid=$1 AND is_paid=false RETURNING user_id, amount",
            uuid,
        )
        if not row:
            return None
        user_id = int(row["user_id"])
        amount = float(row["amount"])
        await conn.execute("UPDATE users SET balance = balance + $1 WHERE telegram_id=$2", amount, user_id)
        return user_id, amount


async def mulenpay_webhook(request: web.Request):
    config = request.app["config"]
    db_pool = request.app["db_pool"]
    bot: Bot = request.app["bot"]

    raw_body = await request.read()
    try:
        data = json.loads(raw_body)
    except Exception:
        return web.Response(status=400, text="Invalid JSON")

    uuid = data.get("uuid")
    status = data.get("status") or data.get("payment_status")
    try:
        amount = float(data.get("amount", 0))
    except Exception:
        amount = 0.0

    if not uuid or status not in ("paid", "success", "done"):
        return web.Response(status=200, text="Ignored")

    credited = await _credit_payment(db_pool, uuid)
    if not credited:
        return web.Response(status=200, text="Already processed")

    user_id, _ = credited

    try:
        await bot.send_photo(
            user_id,
            config.welcome_image_url,
            caption=config.welcome_description,
            reply_markup=main_menu_kb(user_id),
            parse_mode="HTML",
        )
        await bot.send_message(user_id, f"✅ Ваш баланс успешно пополнен на {amount} RUB!")
    except Exception as e:
        print(f"Не удалось отправить сообщение пользователю: {e}")

    return web.Response(status=200, text="OK")


async def heleket_webhook(request: web.Request):
    config = request.app["config"]
    db_pool = request.app["db_pool"]
    bot: Bot = request.app["bot"]

    try:
        data = request.query if request.content_type == "application/x-www-form-urlencoded" else await request.json()
    except Exception:
        return web.Response(status=400, text="Invalid JSON")

    result = data.get("result") or data
    status = result.get("payment_status") or result.get("status")
    uuid = result.get("uuid")

    if not uuid or status not in ("paid", "success", "done"):
        return web.Response(status=200, text="Ignored")

    credited = await _credit_payment(db_pool, uuid)
    if not credited:
        return web.Response(status=200, text="Already processed")

    user_id, amount = credited

    try:
        await bot.send_photo(
            user_id,
            config.welcome_image_url,
            caption=config.welcome_description,
            reply_markup=main_menu_kb(user_id),
            parse_mode="HTML",
        )
        await bot.send_message(user_id, f"✅ Ваш баланс успешно пополнен на {amount} RUB!")
    except Exception as e:
        print(f"Не удалось отправить сообщение пользователю (crypto): {e}")

    return web.Response(status=200, text="OK")


async def cryptobot_webhook(request: web.Request):
    """Crypto Pay API webhook.

    Docs: https://help.send.tg/crypto-pay-api/webhooks (signature check).
    """
    config = request.app["config"]
    db_pool = request.app["db_pool"]
    bot: Bot = request.app["bot"]

    if not config.cryptopay_token:
        return web.Response(status=500, text="CRYPTOPAY_TOKEN not configured")

    raw_body = await request.read()
    signature = request.headers.get("crypto-pay-api-signature")
    if not signature or not verify_webhook_signature(config.cryptopay_token, raw_body, signature):
        return web.Response(status=403, text="Invalid signature")

    try:
        update = json.loads(raw_body)
    except Exception:
        return web.Response(status=400, text="Invalid JSON")

    update_type = update.get("update_type")
    payload = update.get("payload") or {}

    if update_type != "invoice_paid":
        return web.Response(status=200, text="Ignored")

    try:
        invoice_id = int(payload.get("invoice_id"))
    except Exception:
        return web.Response(status=200, text="Ignored")

    payment_uuid = f"cryptobot_{invoice_id}"

    credited = await _credit_payment(db_pool, payment_uuid)
    if not credited:
        return web.Response(status=200, text="Already processed")

    user_id, amount = credited

    try:
        await bot.send_message(user_id, f"✅ Баланс пополнен на {amount} RUB (CryptoBot)")
    except Exception as e:
        print(f"CryptoBot notify error: {e}")

    return web.Response(status=200, text="OK")


async def tonconnect_manifest(request: web.Request):
    config = request.app["config"]
    if not config.public_base_url:
        return web.json_response({"error": "PUBLIC_BASE_URL not configured"}, status=500)

    manifest = {
        "url": config.public_base_url + "/",
        "name": "StarShop",
        "iconUrl": config.public_base_url + "/static/tonconnect/icon.png",
    }
    # CORS: allow wallets to fetch the manifest
    return web.json_response(manifest, headers={"Access-Control-Allow-Origin": "*"})


async def tonconnect_payment_info(request: web.Request):
    config = request.app["config"]
    db_pool = request.app["db_pool"]

    payment_uuid = request.match_info.get("uuid")
    if not payment_uuid:
        return web.Response(status=404)

    try:
        async with db_pool.acquire() as conn:
            meta = await conn.fetchrow("SELECT uuid, ton_amount, comment, tx_hash, paid_at FROM tonconnect_payments WHERE uuid=$1", payment_uuid)
            pay = await conn.fetchrow("SELECT is_paid FROM payments WHERE uuid=$1", payment_uuid)
    except Exception:
        return web.json_response({"error": "DB schema missing: apply database/patch_2026_cryptobot_tonconnect.sql"}, status=500)

    if not meta or not pay:
        return web.Response(status=404)

    data = {
        "uuid": str(meta["uuid"]),
        "ton_amount": str(meta["ton_amount"]),
        "comment": str(meta["comment"]),
        "receiver": config.ton_receiver_address,
        "is_paid": bool(pay["is_paid"]),
        "tx_hash": meta["tx_hash"],
        "paid_at": meta["paid_at"].isoformat() if meta["paid_at"] else None,
    }

    return web.json_response(data, headers={"Access-Control-Allow-Origin": "*"})


async def main():
    config = load_config()

    if not config.bot_token:
        raise RuntimeError("BOT_TOKEN is empty")

    bot = Bot(token=config.bot_token)
    dp = Dispatcher()

    pool = await create_pg_pool(config.postgres_dsn)
    redis = await create_redis_pool(config.redis_url)

    dp["db_pool"] = pool
    dp["redis"] = redis

    dp.include_router(start_router)
    dp.include_router(profile_router)

    asyncio.create_task(promo_cleanup_scheduler(pool))

    # --- aiohttp web server ---
    app = web.Application()
    app["db_pool"] = pool
    app["bot"] = bot
    app["config"] = config

    # static (TonConnect мини-страница)
    static_path = Path(__file__).parent / "static"
    if static_path.exists():
        app.router.add_static("/static/", path=str(static_path), name="static")

    app.router.add_post("/webhook/mulenpay", mulenpay_webhook)
    app.router.add_post("/webhook/heleket", heleket_webhook)

    # Crypto Pay webhook (recommended: secret path)
    cryptobot_path = "/webhook/cryptobot"
    if config.cryptopay_webhook_path_secret:
        cryptobot_path += f"/{config.cryptopay_webhook_path_secret}"
    app.router.add_post(cryptobot_path, cryptobot_webhook)

    # TonConnect manifest + API
    app.router.add_get("/tonconnect-manifest.json", tonconnect_manifest)
    app.router.add_get("/api/tonconnect/payment/{uuid}", tonconnect_payment_info)

    runner = web.AppRunner(app)
    await runner.setup()
    site = web.TCPSite(runner, "0.0.0.0", 8443)
    asyncio.create_task(site.start())

    await dp.start_polling(bot)


if __name__ == "__main__":
    asyncio.run(main())
