# modules/economy.py
import json
from pathlib import Path
from typing import Dict, Optional, Tuple, List

import aiofiles
from aiogram import Router
from aiogram.filters import Command, CommandObject
from aiogram.types import Message, User
from aiogram.utils.markdown import hbold

from bot.filters import IsOwner

# ==================== Конфигурация ====================
ECONOMY_FILE = Path("data/economy.json")
ECONOMY_FILE.parent.mkdir(parents=True, exist_ok=True)
CURRENCY_NAME = "коинов"


# ==================== Хранилище ====================
class Economy:
    def __init__(self):
        self.data: Dict[int, dict] = {}           # user_id → {balance, username, full_name}
        self.username_to_id: Dict[str, int] = {}  # username.lower() → user_id

    async def load(self):
        if not ECONOMY_FILE.exists():
            return
        try:
            async with aiofiles.open(ECONOMY_FILE, "r", encoding="utf-8") as f:
                content = await f.read()
                if not content.strip():
                    return
                raw = json.loads(content)
                self.data = {int(uid): info for uid, info in raw.items()}
                self.username_to_id = {}
                for uid, info in self.data.items():
                    username = info.get("username")
                    if username:
                        self.username_to_id[username.lower()] = uid
        except Exception as e:
            print(f"[Economy] Load error: {e}")

    async def save(self):
        try:
            async with aiofiles.open(ECONOMY_FILE, "w", encoding="utf-8") as f:
                await f.write(json.dumps(self.data, indent=2, ensure_ascii=False))
        except Exception as e:
            print(f"[Economy] Save error: {e}")

    async def ensure_user(self, user_id: int, username: Optional[str] = None, full_name: Optional[str] = None):
        """Создаёт пользователя с 0 балансом, если его нет"""
        if user_id not in self.data:
            self.data[user_id] = {
                "balance": 50,
                "username": username,
                "full_name": full_name or "Unknown User"
            }
            if username:
                self.username_to_id[username.lower()] = user_id
            await self.save()

        # Обновляем данные, если изменились
        updated = False
        if username and self.data[user_id]["username"] != username:
            old = self.data[user_id]["username"]
            if old and old.lower() in self.username_to_id:
                del self.username_to_id[old.lower()]
            self.data[user_id]["username"] = username
            self.username_to_id[username.lower()] = user_id
            updated = True

        if full_name and self.data[user_id]["full_name"] != full_name:
            self.data[user_id]["full_name"] = full_name
            updated = True

        if updated:
            await self.save()

    async def get_balance(self, user: User) -> int:
        await self.ensure_user(user.id, user.username, user.full_name)
        return self.data[user.id]["balance"]

    async def modify_balance(self, user_id: int, delta: int, username: Optional[str] = None, full_name: Optional[str] = None) -> int:
        await self.ensure_user(user_id, username, full_name)
        self.data[user_id]["balance"] += delta
        await self.save()
        return self.data[user_id]["balance"]

    async def set_balance(self, user_id: int, amount: int, username: Optional[str] = None, full_name: Optional[str] = None):
        await self.ensure_user(user_id, username, full_name)
        self.data[user_id]["balance"] = amount
        await self.save()

    async def delete_user(self, user_id: int) -> bool:
        if user_id in self.data:
            username = self.data[user_id].get("username")
            if username and username.lower() in self.username_to_id:
                del self.username_to_id[username.lower()]
            del self.data[user_id]
            await self.save()
            return True
        return False

    def resolve_id(self, username: str) -> Optional[int]:
        return self.username_to_id.get(username.removeprefix("@").lower())

    def get_top(self, limit: int = 20) -> List[Tuple[int, int, str, str]]:
        """Топ только с положительным балансом"""
        items = []
        for uid, info in self.data.items():
            bal = info["balance"]
            if bal <= -1000:
                continue  # ← НЕ ПОКАЗЫВАЕМ НУЛЕВЫЕ БАЛАНСЫ
            items.append((
                uid,
                bal,
                info.get("username") or "",
                info.get("full_name") or f"User#{uid}"
            ))
        return sorted(items, key=lambda x: x[1], reverse=True)[:limit]


economy = Economy()
router = Router(name="economy")


# ==================== Утилиты ====================
def fmt(num: int) -> str:
    return f"{num:,}".replace(",", " ")


def user_mention(user: Optional[User] = None, username: str = "", full_name: str = "") -> str:
    if user:
        if user.username:
            return f"@{user.username}"
        return hbold(user.full_name or "Unknown")
    if username:
        return f"@{username}"
    return hbold(full_name or "Unknown User")


# ==================== Функция для регистрации при любом сообщении ====================
async def register_user_on_message(message: Message):
    """Вызывай эту функцию в глобальном обработчике сообщений"""
    if message.from_user:
        await economy.ensure_user(
            user_id=message.from_user.id,
            username=message.from_user.username,
            full_name=message.from_user.full_name
        )


# ==================== Команды ====================

@router.message(Command("balance"))
async def cmd_balance(message: Message, command: CommandObject):
    target = message.from_user

    if message.reply_to_message and message.reply_to_message.from_user:
        target = message.reply_to_message.from_user
    elif command.args:
        uid = economy.resolve_id(command.args.strip())
        if uid:
            info = economy.data[uid]
            name = user_mention(username=info.get("username"), full_name=info.get("full_name"))
            await message.answer(f"Баланс {name}: {hbold(fmt(info['balance']))} {CURRENCY_NAME}")
            return

    bal = await economy.get_balance(target)
    await message.answer(f"Баланс {user_mention(target)}: {hbold(fmt(bal))} {CURRENCY_NAME}")


async def _get_target(message: Message, arg: Optional[str] = None):
    if message.reply_to_message and message.reply_to_message.from_user:
        return message.reply_to_message.from_user, None, None

    if arg:
        username_raw = arg.strip().removeprefix("@")
        uid = economy.resolve_id("@" + username_raw) or economy.resolve_id(username_raw)
        return None, uid, username_raw

    return message.from_user, None, None


@router.message(Command("setbalance"), IsOwner(send_error_message=True))
async def cmd_setbalance(message: Message, command: CommandObject):
    if not command.args:
        return await message.answer("Использование: /setbalance <сумма> [@username | реплай]")

    parts = command.args.strip().split(maxsplit=2)
    try:
        amount = int(parts[0])
    except ValueError:
        return await message.answer("Сумма должна быть числом.")

    user_obj, uid, username = await _get_target(message, parts[1] if len(parts) > 1 else None)
    target_id = user_obj.id if user_obj else uid

    if not target_id:
        return await message.answer("Пользователь не найден.")

    await economy.set_balance(target_id, amount, username, user_obj.full_name if user_obj else None)
    await message.answer(f"Баланс {user_mention(user_obj, username)} → {hbold(fmt(amount))} {CURRENCY_NAME}")
    return None


@router.message(Command("plusbalance"), IsOwner(send_error_message=True))
async def cmd_plusbalance(message: Message, command: CommandObject):
    if not command.args:
        return await message.answer("Использование: /plusbalance <сумма> [@username | реплай]")

    parts = command.args.strip().split(maxsplit=2)
    try:
        delta = int(parts[0])
    except ValueError:
        return await message.answer("Сумма должна быть числом.")

    user_obj, uid, username = await _get_target(message, parts[1] if len(parts) > 1 else None)
    target_id = user_obj.id if user_obj else uid

    if not target_id:
        return await message.answer("Пользователь не найден.")

    new_bal = await economy.modify_balance(target_id, delta, username, user_obj.full_name if user_obj else None)
    await message.answer(f"{user_mention(user_obj, username)} +{fmt(delta)} → {hbold(fmt(new_bal))} {CURRENCY_NAME}")
    return None


@router.message(Command("minbalance"), IsOwner(send_error_message=True))
async def cmd_minbalance(message: Message, command: CommandObject):
    if not command.args:
        return await message.answer("Использование: /minbalance <сумма> [@username | реплай]")

    parts = command.args.strip().split(maxsplit=2)
    try:
        delta = int(parts[0])
    except ValueError:
        return await message.answer("Сумма должна быть числом.")

    user_obj, uid, username = await _get_target(message, parts[1] if len(parts) > 1 else None)
    target_id = user_obj.id if user_obj else uid

    if not target_id:
        return await message.answer("Пользователь не найден.")

    new_bal = await economy.modify_balance(target_id, -delta, username, user_obj.full_name if user_obj else None)
    await message.answer(f"{user_mention(user_obj, username)} -{fmt(delta)} → {hbold(fmt(new_bal))} {CURRENCY_NAME}")
    return None


@router.message(Command("top"))
async def cmd_top(message: Message):
    top = economy.get_top(20)
    if not top:
        return await message.answer("Топ пустой — никто ещё не заработал коины!")

    lines = ["Топ-20 богачей:"]
    for i, (_, bal, username, full_name) in enumerate(top, 1):
        medal = ["1st", "2nd", "3rd"][i-1] if i <= 3 else f"{i}."
        name = f"@{username}" if username else hbold(full_name)
        lines.append(f"{medal} {name} — {hbold(fmt(bal))} {CURRENCY_NAME}")

    await message.answer("\n".join(lines))
    return None


@router.message(Command("deletebalance"), IsOwner(send_error_message=True))
async def cmd_deletebalance(message: Message):
    user_obj, uid, username = await _get_target(message)
    if not (user_obj or uid):
        return await message.answer("Укажи пользователя реплаем или @username")

    target_id = user_obj.id if user_obj else uid
    deleted = await economy.delete_user(target_id)
    name = user_mention(user_obj, username)
    await message.answer(f"Запись {name} {'удалена' if deleted else 'не существовала'}")
    return None


# ==================== Запуск ====================
async def on_startup(_):
    await economy.load()


__all__ = ["router", "on_startup", "register_user_on_message"]