import asyncio
import logging
import os
import sqlite3
from aiogram import Bot, Dispatcher, F
from aiogram.types import Message, CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.context import FSMContext

# --- Configuration ---
# مقادیر حساس از متغیرهای محیطی (Environment Variables) خوانده می‌شوند
BOT_TOKEN = os.environ.get("BOT_TOKEN")
CHANNEL_ID = os.environ.get("CHANNEL_ID", "@ExchErAr")
DB_NAME = os.environ.get("DB_NAME", "exchange_p2p.db")

if not BOT_TOKEN:
    raise RuntimeError("BOT_TOKEN تنظیم نشده است. لطفاً متغیر محیطی BOT_TOKEN را ست کنید.")

bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
logging.basicConfig(level=logging.WARNING)

# --- Database & Logic ---
def init_db():
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS users (
            user_id INTEGER PRIMARY KEY, 
            username TEXT, 
            successful_deals INTEGER DEFAULT 0, 
            total_rating INTEGER DEFAULT 0
        )
    """)
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS ads (
            ad_id INTEGER PRIMARY KEY AUTOINCREMENT, 
            owner_id INTEGER, 
            channel_message_id INTEGER
        )
    """)
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS ratings_log (
            buyer_id INTEGER,
            seller_id INTEGER,
            PRIMARY KEY (buyer_id, seller_id)
        )
    """)
    conn.commit()
    conn.close()

def has_already_rated(buyer_id, seller_id):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("SELECT 1 FROM ratings_log WHERE buyer_id = ? AND seller_id = ?", (buyer_id, seller_id))
    row = cursor.fetchone()
    conn.close()
    return row is not None

def log_rating(buyer_id, seller_id):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("INSERT OR IGNORE INTO ratings_log (buyer_id, seller_id) VALUES (?, ?)", (buyer_id, seller_id))
    conn.commit()
    conn.close()

def get_user_reputation(user_id):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("SELECT successful_deals, total_rating FROM users WHERE user_id = ?", (user_id,))
    row = cursor.fetchone()
    conn.close()
    if not row or row[0] == 0: 
        return "🆕 (No deals yet)"
    deals = row[0]
    rating_avg = round(row[1] / deals, 1)
    return f"⭐️ {rating_avg}/5 ({deals} successful deals)"

def touch_user(user_id, username):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("""
        INSERT INTO users (user_id, username) 
        VALUES (?, ?) 
        ON CONFLICT(user_id) DO UPDATE SET username = excluded.username
    """, (user_id, username))
    conn.commit()
    conn.close()

def save_ad(owner_id, msg_id):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("INSERT INTO ads (owner_id, channel_message_id) VALUES (?, ?)", (owner_id, msg_id))
    conn.commit()
    conn.close()

def get_ad_info(msg_id):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("SELECT owner_id FROM ads WHERE channel_message_id = ?", (msg_id,))
    row = cursor.fetchone()
    conn.close()
    return row[0] if row else None

def update_rating(user_id, score):
    conn = sqlite3.connect(DB_NAME)
    cursor = conn.cursor()
    cursor.execute("""
        UPDATE users 
        SET successful_deals = successful_deals + 1, total_rating = total_rating + ? 
        WHERE user_id = ?
    """, (score, user_id))
    conn.commit()
    conn.close()

init_db()

# --- Keyboards & Constants ---
CURRENCIES = {
    "USDT": "🟢 USDT",
    "USD": "🇺🇸 USD",
    "IRR": "🇮🇷 IRR",
    "AED": "🇦🇪 AED",
    "AMD": "🇦🇲 AMD",
    "RUB": "🇷🇺 RUB",
    "TL": "🇹🇷 TL",
    "GEL": "🇬🇪 GEL",
    "OTHER": "🌐 OTHER"
}

class AdForm(StatesGroup):
    source = State()
    target = State()
    city = State()
    custom_city = State()
    amount = State()
    rate = State()
    buyer_user = State()

def format_number(value: str) -> str:
    clean_value = value.replace(",", "").strip()
    try:
        if '.' in clean_value:
            parts = clean_value.split('.')
            return f"{int(parts[0]):,}.{parts[1]}"
        return f"{int(clean_value):,}"
    except ValueError:
        return value

def get_source_keyboard():
    buttons = [[InlineKeyboardButton(text=text, callback_data=f"src_{code}")] for code, text in CURRENCIES.items()]
    return InlineKeyboardMarkup(inline_keyboard=[*buttons])

def get_target_keyboard(source_curr):
    buttons = [[InlineKeyboardButton(text=text, callback_data=f"tgt_{code}")] for code, text in CURRENCIES.items() if code != source_curr]
    return InlineKeyboardMarkup(inline_keyboard=[*buttons])

def get_cities_keyboard():
    return InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="Tehran 🇮🇷", callback_data="city_Tehran 🇮🇷")],
        [InlineKeyboardButton(text="Yerevan 🇦🇲", callback_data="city_Yerevan 🇦🇲")],
        [InlineKeyboardButton(text="Istanbul 🇹🇷", callback_data="city_Istanbul 🇹🇷")],
        [InlineKeyboardButton(text="Tbilisi 🇬🇪", callback_data="city_Tbilisi 🇬🇪")],
        [InlineKeyboardButton(text="Moscow 🇷🇺", callback_data="city_Moscow 🇷🇺")],
        [InlineKeyboardButton(text="Kazan 🇷🇺", callback_data="city_Kazan 🇷🇺")],
        [InlineKeyboardButton(text="Dubai 🇦🇪", callback_data="city_Dubai 🇦🇪")],
        [InlineKeyboardButton(text="Los Angeles 🇺🇸", callback_data="city_Los Angeles 🇺🇸")],
        [InlineKeyboardButton(text="🌐 Other (Type City)", callback_data="city_other")]
    ])

# --- Handlers ---

@dp.message(F.text.startswith("/start"))
async def cmd_start(message: Message, state: FSMContext):
    await state.clear()
    
    args = message.text.split(" ")
    if len(args) > 1 and args[1].startswith("rate_"):
        try:
            target_user_id = int(args[1].split("_")[1])
            if message.from_user.id == target_user_id:
                await message.answer("⚠️ You cannot rate yourself!")
                return
            
            if has_already_rated(message.from_user.id, target_user_id):
                await message.answer("⚠️ You have already submitted a rating for this deal!")
                return
            
            stars_kb = InlineKeyboardMarkup(inline_keyboard=[
                [InlineKeyboardButton(text=f"⭐ {i}", callback_data=f"rate_{target_user_id}_{i}") for i in range(1, 6)]
            ])
            await message.answer("🤝 <b>Rate your trading experience!</b>\n\nHow was your transaction?\nPlease select stars:", reply_markup=stars_kb, parse_mode="HTML")
            return
        except Exception:
            pass

    await message.answer("✨ <b>Select the currency you HAVE:</b>", reply_markup=get_source_keyboard(), parse_mode="HTML")
    await state.set_state(AdForm.source)

@dp.callback_query(AdForm.source, F.data.startswith("src_"))
async def set_src(call: CallbackQuery, state: FSMContext):
    src_code = call.data.split("_")[1]
    await state.update_data(src_code=src_code)
    await call.message.edit_text(f"Have: <b>{CURRENCIES[src_code]}</b>\n\n✨ <b>Select the currency you WANT:</b>", reply_markup=get_target_keyboard(src_code), parse_mode="HTML")
    await state.set_state(AdForm.target)

@dp.callback_query(AdForm.target, F.data.startswith("tgt_"))
async def set_tgt(call: CallbackQuery, state: FSMContext):
    tgt_code = call.data.split("_")[1]
    user_data = await state.get_data()
    src_code = user_data['src_code']
    pair_text = f"{CURRENCIES[src_code]} ⇄ {CURRENCIES[tgt_code]}"
    await state.update_data(pair_text=pair_text)
    await call.message.edit_text(f"Selected: <b>{pair_text}</b>\n\n📍 <b>Select City:</b>", reply_markup=get_cities_keyboard(), parse_mode="HTML")
    await state.set_state(AdForm.city)

@dp.callback_query(AdForm.city, F.data.startswith("city_"))
async def set_city_callback(call: CallbackQuery, state: FSMContext):
    if call.data == "city_other":
        await call.message.edit_text("🌐 Please <b>Type</b> the name of your city:", parse_mode="HTML", reply_markup=None)
        await state.set_state(AdForm.custom_city)
    else:
        chosen_city = call.data.replace("city_", "")
        await state.update_data(city_name=chosen_city)
        user_data = await state.get_data()
        await call.message.edit_text(f"Selected: <b>{user_data['pair_text']}</b>\nCity: <b>{chosen_city}</b>\n\n💰 <b>Enter Amount (Numbers only):</b>", parse_mode="HTML", reply_markup=None)
        await state.set_state(AdForm.amount)
    await call.answer()

@dp.message(AdForm.custom_city)
async def set_custom_city(msg: Message, state: FSMContext):
    city_format = f"{msg.text} 🌐"
    await state.update_data(city_name=city_format)
    user_data = await state.get_data()
    await msg.answer(f"Selected: <b>{user_data['pair_text']}</b>\nCity: <b>{city_format}</b>\n\n💰 <b>Enter Amount (Numbers only):</b>", parse_mode="HTML")
    await state.set_state(AdForm.amount)

@dp.message(AdForm.amount)
async def set_amount(msg: Message, state: FSMContext):
    clean_amount = msg.text.replace(",", "")
    try:
        float(clean_amount)
    except ValueError:
        await msg.answer("⚠️ <b>Invalid Amount!</b> Please enter numbers only:", parse_mode="HTML")
        return
    await state.update_data(amount_value=msg.text)
    user_data = await state.get_data()
    await msg.answer(f"Selected: <b>{user_data['pair_text']}</b>\nCity: <b>{user_data['city_name']}</b>\nAmount: <code>{format_number(msg.text)}</code>\n\n🔹 <b>Enter your target Rate (or click below):</b>", reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text="Negotiable Rate 🤝", callback_data="rate_neg")]]), parse_mode="HTML")
    await state.set_state(AdForm.rate)

@dp.callback_query(AdForm.rate, F.data == "rate_neg")
async def set_rate_neg(call: CallbackQuery, state: FSMContext):
    await publish_ad(call.message, await state.get_data(), "Negotiable 🤝", call.from_user)
    await call.answer()
    await state.clear()

@dp.message(AdForm.rate)
async def set_rate(msg: Message, state: FSMContext):
    await publish_ad(msg, await state.get_data(), msg.text, msg.from_user)
    await state.clear()

async def publish_ad(ctx, data, rate_value, user):
    touch_user(user.id, user.username)
    reputation_text = get_user_reputation(user.id)
    
    post_text = (
        f"╔══════════════════════╗\n\n"
        f"🔀 <b>PAIR:</b>  {data['pair_text']}\n\n"
        f"📍 <b>CITY:</b>  <code>{data['city_name']}</code>\n\n"
        f"💰 <b>AMOUNT:</b>  <code>{format_number(data['amount_value'])}</code>\n\n"
        f"💵 <b>RATE:</b>  <code>{format_number(rate_value)}</code>\n\n"
        f"🛡️ <b>REPUTATION:</b>  {reputation_text}\n\n"
        f"╚══════════════════════╝"
    )
    
    msg = await bot.send_message(
        chat_id=CHANNEL_ID, 
        text=post_text, 
        reply_markup=InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton(text="💬 Send Message / Contact", url=f"https://t.me/{user.username}")]
        ]), 
        parse_mode="HTML"
    )
    save_ad(user.id, msg.message_id)
    
    # ساختن لینک مستقیم به پیام در کانال عمومی
    channel_clean = CHANNEL_ID.replace("@", "")
    post_url = f"https://t.me/{channel_clean}/{msg.message_id}"
    
    # اضافه شدن دکمه ورود به کانال در کنار دکمه پایان معامله
    success_kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="📢 View in Channel", url=post_url)],
        [InlineKeyboardButton(text="🗑️ Delete Ad & Complete Deal", callback_data=f"finish_{msg.message_id}")]
    ])
    
    await ctx.answer(
        f"🚀 <b>Ad successfully published!</b>\n\n"
        f"You can view your post in our channel: {CHANNEL_ID}\n\n"
        f"When the deal is completed, click below to delete it from the channel and start rating:",
        reply_markup=success_kb,
        parse_mode="HTML"
    )

@dp.callback_query(F.data.startswith("finish_"))
async def finish_deal(call: CallbackQuery, state: FSMContext):
    mid = int(call.data.split("_")[1])
    if call.from_user.id != get_ad_info(mid): 
        return await call.answer("⚠️ Not authorized!", show_alert=True)
    
    try:
        await bot.delete_message(CHANNEL_ID, mid)
        await call.message.edit_text("🗑️ <b>Ad deleted from channel.</b>\n\n👤 Enter the buyer's Telegram username (without @):", parse_mode="HTML")
        await state.update_data(oid=call.from_user.id)
        await state.set_state(AdForm.buyer_user)
    except Exception:
        await call.message.answer("❌ Error deleting ad or it was already deleted.")
    await call.answer()

@dp.message(AdForm.buyer_user)
async def send_rating(msg: Message, state: FSMContext):
    oid = (await state.get_data())['oid']
    target = msg.text.replace("@", "").strip()
    
    bot_info = await bot.get_me()
    secure_link = f"https://t.me/{bot_info.username}?start=rate_{oid}"
    
    forward_share_kb = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="⭐ Submit Stars", url=secure_link)]
    ])
    
    await msg.answer(
        f"🔗 <b>Rating link generated successfully!</b>\n\n"
        f"Please <b>FORWARD</b> this message to the buyer ( @{target} ) or click the button below:\n\n"
        f"{secure_link}",
        reply_markup=forward_share_kb,
        parse_mode="HTML",
        disable_web_page_preview=True 
    )
    await state.clear()

@dp.callback_query(F.data.startswith("rate_"))
async def process_rate(call: CallbackQuery):
    _, oid, score = call.data.split("_")
    seller_id = int(oid)
    buyer_id = call.from_user.id
    
    if has_already_rated(buyer_id, seller_id):
        await call.message.edit_text("⚠️ You have already submitted a rating for this deal!")
        await call.answer()
        return
        
    log_rating(buyer_id, seller_id)
    update_rating(seller_id, int(score))
    await call.message.edit_text(f"✅ <b>Thank you!</b> You submitted a ⭐ <b>{score}/5</b> rating.", parse_mode="HTML")
    await call.answer()

async def main():
    bot_info = await bot.get_me()
    print(f"🚀 Bot is successfully running on @{bot_info.username}...")
    await bot.delete_webhook(drop_pending_updates=True)
    await dp.start_polling(bot)

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