#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
GALAXY POINT - Telegram points bot
Commands:
  /start   - Describe the bot
  /help    - Show available commands
  /pocket  - See your point balance
  /ptgive  - Give points to a replied player
  /daily   - Claim 40-100 points once every 24 hours

Configuration is loaded from pt.env in the parent HTML folder.
"""

import os
import sys
import random
import logging
import sqlite3
from datetime import datetime, timedelta

from dotenv import load_dotenv
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ENV_PATH = os.path.abspath(os.path.join(BASE_DIR, os.pardir, "pt.env"))
DB_PATH = os.path.join(BASE_DIR, "galaxy_point.db")

load_dotenv(ENV_PATH)
TOKEN = os.getenv("BOT_TOKEN")
OWNER_ID = int(os.getenv("OWNER_ID", "0"))
ADMIN_IDS = [int(x.strip()) for x in os.getenv("ADMIN_IDS", "").split(",") if x.strip()]
ADMIN_IDS = set(ADMIN_IDS)

if not TOKEN:
    print("Error: BOT_TOKEN not set in pt.env")
    sys.exit(1)

logging.basicConfig(
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    level=logging.INFO,
)
logger = logging.getLogger(__name__)

conn = sqlite3.connect(DB_PATH, check_same_thread=False)
cursor = conn.cursor()

cursor.execute(
    """
    CREATE TABLE IF NOT EXISTS users (
        user_id INTEGER PRIMARY KEY,
        username TEXT,
        first_name TEXT,
        points INTEGER DEFAULT 0,
        last_daily_ts REAL DEFAULT 0
    )
    """
)
conn.commit()

# Banned users table for cross-bot ban synchronization
cursor.execute(
    """
    CREATE TABLE IF NOT EXISTS banned_users (
        user_id INTEGER PRIMARY KEY,
        ban_until REAL DEFAULT NULL
    )
    """
)
conn.commit()


def format_user_name(user) -> str:
    if user.username:
        return f"@{user.username}"
    if user.first_name:
        return user.first_name
    return "Player"


def get_user_record(user_id: int):
    cursor.execute("SELECT user_id, username, first_name, points, last_daily_ts FROM users WHERE user_id = ?", (user_id,))
    return cursor.fetchone()


def ensure_user(user):
    if user is None:
        return
    cursor.execute(
        "INSERT OR IGNORE INTO users (user_id, username, first_name) VALUES (?, ?, ?)",
        (user.id, user.username or "", user.first_name or ""),
    )
    cursor.execute(
        "UPDATE users SET username = ?, first_name = ? WHERE user_id = ?",
        (user.username or "", user.first_name or "", user.id),
    )
    conn.commit()


def get_points(user_id: int) -> int:
    row = get_user_record(user_id)
    return row[3] if row else 0


def is_banned_wallet(user_id: int) -> (bool, float):
    cursor.execute("SELECT ban_until FROM banned_users WHERE user_id = ?", (user_id,))
    row = cursor.fetchone()
    if not row:
        return False, None
    ban_until = row[0]
    if ban_until is None:
        return True, None
    from time import time
    if time() > ban_until:
        cursor.execute("DELETE FROM banned_users WHERE user_id = ?", (user_id,))
        conn.commit()
        return False, None
    return True, ban_until


def ban_user_wallet(user_id: int, until: float = None):
    cursor.execute("INSERT OR REPLACE INTO banned_users (user_id, ban_until) VALUES (?, ?)", (user_id, until))
    conn.commit()


def unban_user_wallet(user_id: int):
    cursor.execute("DELETE FROM banned_users WHERE user_id = ?", (user_id,))
    conn.commit()


def add_points(user_id: int, amount: int):
    cursor.execute("UPDATE users SET points = points + ? WHERE user_id = ?", (amount, user_id))
    conn.commit()


def set_last_daily(user_id: int, timestamp: float):
    cursor.execute("UPDATE users SET last_daily_ts = ? WHERE user_id = ?", (timestamp, user_id))
    conn.commit()


def can_claim_daily(user_id: int) -> (bool, timedelta):
    row = get_user_record(user_id)
    if not row:
        return True, timedelta(0)
    last_ts = row[4] or 0
    last_time = datetime.utcfromtimestamp(last_ts)
    next_time = last_time + timedelta(hours=24)
    now = datetime.utcnow()
    if now >= next_time:
        return True, timedelta(0)
    return False, next_time - now


def build_command_keyboard():
    keyboard = [
        [InlineKeyboardButton("💰 Pocket", callback_data="pocket")],
        [InlineKeyboardButton("📜 Help", callback_data="help")],
        [InlineKeyboardButton("🌞 Daily", callback_data="daily")],
    ]
    return InlineKeyboardMarkup(keyboard)


def human_timedelta(delta: timedelta) -> str:
    total = int(delta.total_seconds())
    hours = total // 3600
    minutes = (total % 3600) // 60
    seconds = total % 60
    parts = []
    if hours:
        parts.append(f"{hours}h")
    if minutes:
        parts.append(f"{minutes}m")
    if seconds or not parts:
        parts.append(f"{seconds}s")
    return " ".join(parts)


async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    ensure_user(user)

    text = (
        "<b>🌌 Welcome to GALAXY POINT</b>\n\n"
        "A lightweight point bot for friendly gifting and daily rewards.\n\n"
        "<b>Commands</b>\n"
        "• /pocket - Check your points\n"
        "• /ptgive - Give points to a replied player\n"
        "• /daily - Claim 40-100 points once every 24 hours\n\n"
        "<b>Tip</b>: Reply to a player's message with <code>/ptgive 50</code> to send them points instantly."
    )
    await update.message.reply_text(text, parse_mode="HTML", reply_markup=build_command_keyboard())


async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    ensure_user(user)

    help_text = (
        "<b>GALAXY POINT Help</b>\n\n"
        "<b>/start</b> - Describe the bot\n"
        "<b>/help</b> - Show this help message\n"
        "<b>/pocket</b> - See your current points\n"
        "<b>/daily</b> - Claim random points between 40 and 100 once every 24 hours\n"
        "<b>/ptgive</b> - Give points to someone by replying to their message\n\n"
        "<b>Usage</b>: Reply to a player's message and send <code>/ptgive 50</code>."
    )
    await update.message.reply_text(help_text, parse_mode="HTML", reply_markup=build_command_keyboard())


async def pocket(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    ensure_user(user)

    points = get_points(user.id)
    can_claim, wait = can_claim_daily(user.id)
    daily_status = (
        "Ready to claim now!" if can_claim else f"Available in {human_timedelta(wait)}"
    )

    message = (
        f"<b>💼 {format_user_name(user)}'s Pocket</b>\n\n"
        f"<b>Points:</b> {points}\n"
        f"<b>Daily reward:</b> {daily_status}\n\n"
        "Use /daily to claim your next reward.\n"
        "Reply to someone with <code>/ptgive 50</code> to share points."
    )
    await update.message.reply_text(message, parse_mode="HTML")


async def daily(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    ensure_user(user)

    can_claim, wait = can_claim_daily(user.id)
    if not can_claim:
        await update.message.reply_text(
            f"⏳ You already claimed your daily reward. Come back in {human_timedelta(wait)}.",
            parse_mode="HTML",
        )
        return

    amount = random.randint(40, 100)
    add_points(user.id, amount)
    set_last_daily(user.id, datetime.utcnow().timestamp())

    await update.message.reply_text(
        f"🎉 Daily reward claimed! You received <b>{amount}</b> points.\n"
        f"Your new balance is <b>{get_points(user.id)}</b> points.",
        parse_mode="HTML",
    )


async def ptgive(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user = update.effective_user
    ensure_user(user)

    reply = update.message.reply_to_message
    if reply is None or reply.from_user is None:
        await update.message.reply_text(
            "⚠️ To give points, reply to a player's message with <code>/ptgive 50</code>.",
            parse_mode="HTML",
        )
        return

    if len(context.args) < 1:
        await update.message.reply_text("⚠️ Please specify the amount of points to give.")
        return

    try:
        amount = int(context.args[0])
    except ValueError:
        await update.message.reply_text("⚠️ Invalid amount. Use a number like <code>/ptgive 50</code>.", parse_mode="HTML")
        return

    if amount <= 0:
        await update.message.reply_text("⚠️ Amount must be greater than zero.")
        return

    receiver = reply.from_user
    if receiver.id == user.id:
        await update.message.reply_text("⚠️ You cannot give points to yourself.")
        return

    sender_points = get_points(user.id)
    if sender_points < amount:
        await update.message.reply_text(
            f"❌ You only have {sender_points} points, but tried to send {amount}."
        )
        return

    ensure_user(receiver)
    add_points(user.id, -amount)
    add_points(receiver.id, amount)

    await update.message.reply_text(
        f"✨ {format_user_name(user)} sent <b>{amount}</b> points to {format_user_name(receiver)}!\n"
        f"Your remaining balance is <b>{get_points(user.id)}</b> points.",
        parse_mode="HTML",
    )


async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    if query is None:
        return
    await query.answer()

    data = query.data
    user = update.effective_user
    ensure_user(user)

    if data == "pocket":
        points = get_points(user.id)
        can_claim, wait = can_claim_daily(user.id)
        daily_status = (
            "Ready to claim now!" if can_claim else f"Available in {human_timedelta(wait)}"
        )
        await query.message.reply_text(
            f"<b>💼 {format_user_name(user)}'s Pocket</b>\n\n"
            f"<b>Points:</b> {points}\n"
            f"<b>Daily reward:</b> {daily_status}\n\n"
            "Use /daily to claim your next reward.\n"
            "Reply to someone with <code>/ptgive 50</code> to share points.",
            parse_mode="HTML",
        )
        return

    if data == "help":
        await query.message.reply_text(
            "<b>GALAXY POINT Help</b>\n\n"
            "<b>/start</b> - Describe the bot\n"
            "<b>/help</b> - Show this help message\n"
            "<b>/pocket</b> - See your current points\n"
            "<b>/daily</b> - Claim random points between 40 and 100 once every 24 hours\n"
            "<b>/ptgive</b> - Give points to someone by replying to their message\n\n"
            "<b>Usage</b>: Reply to a player's message and send <code>/ptgive 50</code>.",
            parse_mode="HTML",
        )
        return

    if data == "daily":
        can_claim, wait = can_claim_daily(user.id)
        if not can_claim:
            await query.message.reply_text(
                f"⏳ You already claimed your daily reward. Come back in {human_timedelta(wait)}.",
                parse_mode="HTML",
            )
            return

        amount = random.randint(40, 100)
        add_points(user.id, amount)
        set_last_daily(user.id, datetime.utcnow().timestamp())
        await query.message.reply_text(
            f"🎉 Daily reward claimed! You received <b>{amount}</b> points.\n"
            f"Your new balance is <b>{get_points(user.id)}</b> points.",
            parse_mode="HTML",
        )
        return


def main():
    application = Application.builder().token(TOKEN).build()

    application.add_handler(CommandHandler("start", start))
    application.add_handler(CommandHandler("help", help_command))
    application.add_handler(CommandHandler("pocket", pocket))
    application.add_handler(CommandHandler("daily", daily))
    application.add_handler(CommandHandler("ptgive", ptgive))
    application.add_handler(CallbackQueryHandler(button_callback, pattern="^(pocket|help|daily)$"))

    logger.info("GALAXY POINT bot starting...")
    application.run_polling(drop_pending_updates=True)


if __name__ == "__main__":
    main()
