#!/usr/bin/env python3
"""
BShop Wholesale API - Lightweight Tester & CLI Client
-----------------------------------------------------
A simple, zero-dependency Python script to test and interact with the
BShop Wholesale API using pre-configured mock dealer credentials.

Requirements: Python 3.8+ (uses standard library `urllib`, no pip required!)
"""

import sys
import json
import urllib.request
import urllib.error
from typing import Any, Dict, List, Optional

# Ensure immediate unbuffered console output
if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(line_buffering=True)

# ==========================================
# CONFIGURATION (Pre-configured Mock Account)
# ==========================================
BASE_URL = "https://staging-api.bshopmyanmar.com"
API_KEY = "bshop_ws_eHeJqrQNSChtfL52mbuboFC8SA0Amr57JvEHiQgycS0"
API_SECRET = "bshop_sec_-eDea8fWAfRlLb7_iu4YGAZn0ISTbHB9SJ0kfpBMRCi2zxFNlwn8N3-iWNoV5wJq"

HEADERS = {
    "Accept": "application/json",
    "Content-Type": "application/json",
    "User-Agent": "BShopWholesaleTester/2.0",
    "X-Wholesale-Key": API_KEY,
    "X-Wholesale-Secret": API_SECRET,
}


def make_request(method: str, path: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
    """Execute an HTTP request using Python standard library."""
    url = f"{BASE_URL.rstrip('/')}/{path.lstrip('/')}"
    body_bytes = json.dumps(data).encode("utf-8") if data is not None else None
    req = urllib.request.Request(url, data=body_bytes, headers=HEADERS, method=method.upper())

    try:
        with urllib.request.urlopen(req, timeout=45) as response:
            status_code = response.getcode()
            raw = response.read().decode("utf-8")
            return {"status": status_code, "body": json.loads(raw)}
    except urllib.error.HTTPError as e:
        raw = e.read().decode("utf-8")
        try:
            parsed = json.loads(raw)
        except Exception:
            parsed = {"raw": raw}
        return {"status": e.code, "error": parsed}
    except Exception as e:
        return {"status": 0, "error": str(e)}


def print_section(title: str):
    print("\n" + "=" * 60)
    print(f"  {title}")
    print("=" * 60)


def check_balance():
    """1. Inspect Dealer Profile & Pre-Loaded Balance."""
    print_section("[1] GET /api/wholesale/balance")
    res = make_request("GET", "/api/wholesale/balance")
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        dealer = data.get("dealer", {})
        tier = data.get("tier", {})
        stock = data.get("stock", {})
        print(f"Dealer         : {dealer.get('first_name')} (@{dealer.get('username')})")
        print(f"User ID        : {dealer.get('user_id')}")
        print(f"Tier           : {tier.get('name', 'Dealer')} (ID: {tier.get('id', 1)})")
        print(f"Wallet Balance : {dealer.get('balance_mmk', 0):,.2f} MMK")
        print(f"Lifetime Spent : {dealer.get('lifetime_spent_mmk', 0):,.2f} MMK")
        print(f"Available Stock: {stock.get('total_available_robux', 0):,} R$")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def get_robux_rate():
    """2. Get Live Robux Wholesale Rate."""
    print_section("[2] GET /api/wholesale/robux/rate")
    res = make_request("GET", "/api/wholesale/robux/rate")
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        rate = data.get("unit_rate_mmk")
        print(f"Wholesale Rate     : {rate} MMK per 1 Robux")
        print(f"Calculation Example: 1,000 Robux = {round(1000 * rate, 2):,.2f} MMK")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def quote_robux(amount_robux: int = 1000):
    """3. Calculate Price Quote for Robux Purchase."""
    print_section(f"[3] POST /api/wholesale/robux/quote (Robux: {amount_robux:,})")
    payload = {"amount_robux": amount_robux}
    res = make_request("POST", "/api/wholesale/robux/quote", payload)
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        print(f"Requested Robux : {data.get('total_robux'):,} R$")
        print(f"Unit Rate       : {data.get('unit_rate_mmk')} MMK/R$")
        print(f"Total Amount    : {data.get('total_amount_mmk'):,.2f} MMK")
        print(f"Can Pay Wallet  : {'YES' if data.get('can_pay_with_balance') else 'NO'}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def check_group_requirements():
    """4. Inspect Active Payout Groups, Join Links & 15-Day Policy."""
    print_section("[4] GET /api/wholesale/group-requirements")
    res = make_request("GET", "/api/wholesale/group-requirements")
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        groups = data.get("groups", [])
        reqs = data.get("requirements", {}) or data.get("policy", {})
        print(f"Active Payout Groups: {len(groups)}")
        for idx, g in enumerate(groups, 1):
            print(f"  Group #{idx}: {g.get('group_name')} (ID: {g.get('group_id')})")
            print(f"    Join URL     : {g.get('join_url')}")
            print(f"    Current Stock: {g.get('balance_robux', 0):,} R$")
            print(f"    Hold Cooldown: {g.get('min_holding_days', 15)} days (360 hours)")
        print(f"Policy Cooldown : {reqs.get('min_membership_days', 15)} days (360 hours)")
        print(f"Policy Notice   : {reqs.get('policy', '')}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def check_eligibility(username: str = "builderman"):
    """5. Verify Single Roblox Account & 15-Day Hold Cooldown."""
    print_section(f"[5] POST /api/wholesale/eligibility/check (Username: {username})")
    payload = {"username": username}
    res = make_request("POST", "/api/wholesale/eligibility/check", payload)
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        print(f"Roblox Username : {data.get('username')}")
        print(f"Roblox User ID  : {data.get('roblox_user_id')}")
        print(f"Eligible        : {'YES' if data.get('is_eligible') else 'NO'}")
        print(f"Group Member    : {'YES' if data.get('is_member') else 'NO'}")
        print(f"In Cooldown     : {'YES' if data.get('in_cooldown') else 'NO'}")
        print(f"Reason / Status : {data.get('reason') or data.get('status')}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def check_batch_eligibility(recipients: Optional[List[Dict[str, Any]]] = None, text: Optional[str] = None):
    """6. Batch Pre-Flight Eligibility Scan (Multiple Accounts)."""
    print_section("[6] POST /api/wholesale/eligibility/batch")
    if recipients is None and text is None:
        recipients = [
            {"username": "builderman", "amount_robux": 100},
            {"username": "roblox", "amount_robux": 100},
            {"username": "GhostPlayer999", "amount_robux": 100},
        ]
    payload = {"recipients": recipients, "text": text}
    res = make_request("POST", "/api/wholesale/eligibility/batch", payload)
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        summary = data.get("summary", {})
        print(f"All Accounts Eligible : {'YES' if data.get('all_eligible') else 'NO'}")
        print(f"Summary               : Total {summary.get('total', 0)} | Eligible {summary.get('eligible', 0)} | Ineligible {summary.get('ineligible', 0)}")
        print("Recipient Results:")
        for r in data.get("results", []):
            st = "ELIGIBLE" if r.get("status") == "eligible" else r.get("status", "ineligible").upper()
            uid = f" (ID: {r.get('roblox_user_id')})" if r.get('roblox_user_id') else ""
            print(f"  - @{r.get('username')}{uid}: {st} | R$: {r.get('amount_robux', 0):,} | Reason: {r.get('reason') or 'Pass'}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def check_transactions(limit: int = 5):
    """7. View Recent Wallet Statement Ledger Transactions."""
    print_section(f"[7] GET /api/wholesale/balance/transactions?limit={limit}")
    res = make_request("GET", f"/api/wholesale/balance/transactions?limit={limit}")
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        txs = data.get("transactions", [])
        print(f"Found {len(txs)} transactions:")
        for t in txs:
            amt = t.get("amount_mmk", 0)
            sign = "+" if amt > 0 else ""
            print(f"  ID #{t.get('id')}: {t.get('type')} {sign}{amt:,.2f} Ks | After: {t.get('balance_after_mmk', 0):,.2f} Ks | {t.get('notes')}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def place_test_order(username: str = "builderman", amount_robux: int = 100, validate_eligibility: bool = False):
    """8. Place Single-Recipient Wholesale Robux Order."""
    print_section(f"[8] POST /api/wholesale/robux/order (Single: {username}, Robux: {amount_robux})")
    payload = {
        "recipients": [
            {"username": username, "amount_robux": amount_robux}
        ],
        "validate_eligibility": validate_eligibility,
        "pay_with_balance": True,
        "notes": "Automated Single Wholesale Test Order"
    }
    res = make_request("POST", "/api/wholesale/robux/order", payload)
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        print(f"Order Number   : {data.get('order_number')}")
        print(f"Status         : {data.get('status')}")
        print(f"Total Robux    : {data.get('total_robux'):,} R$")
        print(f"Debit Amount   : {data.get('balance_deducted_mmk', 0):,.2f} MMK")
        print(f"Remaining Bal  : {data.get('new_balance_mmk', 0):,.2f} MMK")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def place_batch_test_order(recipients: Optional[List[Dict[str, Any]]] = None, validate_eligibility: bool = False):
    """9. Place Multi-Recipient Batch Wholesale Robux Order."""
    print_section("[9] POST /api/wholesale/robux/order (Batch Multi-Recipient)")
    if recipients is None:
        recipients = [
            {"username": "builderman", "amount_robux": 100},
            {"username": "roblox", "amount_robux": 100},
        ]
    payload = {
        "recipients": recipients,
        "validate_eligibility": validate_eligibility,
        "pay_with_balance": True,
        "notes": "Automated Batch Wholesale Test Order"
    }
    res = make_request("POST", "/api/wholesale/robux/order", payload)
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        print(f"Order Number     : {data.get('order_number')}")
        print(f"Status           : {data.get('status')}")
        print(f"Recipients Count : {data.get('recipients_count')}")
        print(f"Total Robux      : {data.get('total_robux'):,} R$")
        print(f"Debit Amount     : {data.get('balance_deducted_mmk', 0):,.2f} MMK")
        print(f"Remaining Bal    : {data.get('new_balance_mmk', 0):,.2f} MMK")
        if data.get("order_number"):
            get_order_detail(data["order_number"])
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def get_order_detail(order_number: str):
    """10. Inspect Status & Recipients of a Specific Order."""
    print_section(f"[10] GET /api/wholesale/robux/orders/{order_number}")
    res = make_request("GET", f"/api/wholesale/robux/orders/{order_number}")
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        print(f"Order Number : {data.get('order_number')}")
        print(f"Status       : {data.get('status')}")
        print(f"Total Amount : {data.get('total_amount_mmk', 0):,.2f} MMK")
        print(f"Recipients ({len(data.get('recipients', []))}):")
        for r in data.get("recipients", []):
            print(f"  - @{r.get('recipient_username')}: {r.get('amount_robux')} R$ | Status: {r.get('status')}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def check_security_status():
    """11. Inspect Security Posture, Detected Client IP, and Allowed IP Whitelist."""
    print_section("[11] GET /api/wholesale/security/status")
    res = make_request("GET", "/api/wholesale/security/status")
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        print(f"User ID        : {data.get('user_id')}")
        print(f"Detected IP    : {data.get('client_ip')}")
        print(f"Auth Type      : {data.get('auth_type')}")
        print(f"Has Secret     : {data.get('has_secret')}")
        allowed = data.get("allowed_ips", [])
        if allowed:
            print(f"Allowed IPs ({len(allowed)}): {', '.join(allowed)}")
        else:
            print("Allowed IPs    : All IPs permitted (no restriction)")
        print("Rate Limits    :")
        for k, v in data.get("rate_limits", {}).items():
            print(f"  - {k:12}: {v}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def update_allowed_ips(allowed_ips: Optional[List[str]] = None):
    """12. Configure Dealer Allowed IP/CIDR Whitelist (Requires X-Wholesale-Secret)."""
    print_section("[12] POST /api/wholesale/security/allowed-ips")
    if allowed_ips is None:
        allowed_ips = ["127.0.0.1", "198.51.100.42", "203.0.113.0/24"]
    payload = {"allowed_ips": allowed_ips}
    res = make_request("POST", "/api/wholesale/security/allowed-ips", payload)
    print(f"Status Code: {res['status']}")
    if res["status"] == 200:
        data = res["body"].get("data", {})
        print(f"Updated Allowed IPs: {data.get('allowed_ips')}")
        print(f"Message            : {data.get('message')}")
    else:
        print("Response:", json.dumps(res, indent=2))
    return res


def run_all_tests():
    print("=" * 65)
    print("  BSHOP WHOLESALE API - AUTOMATED VERIFICATION SUITE")
    print(f"  Target URL : {BASE_URL}")
    print(f"  API Key    : {API_KEY[:14]}...{API_KEY[-4:]}")
    print(f"  API Secret : {API_SECRET[:15]}...{API_SECRET[-4:]}")
    print("=" * 65)

    check_balance()
    check_security_status()
    get_robux_rate()
    quote_robux(1000)
    check_group_requirements()
    check_eligibility("builderman")
    check_batch_eligibility()
    check_transactions(5)
    place_test_order("builderman", 100, validate_eligibility=False)
    place_batch_test_order(
        [
            {"username": "builderman", "amount_robux": 100},
            {"username": "roblox", "amount_robux": 100},
        ],
        validate_eligibility=False,
    )
    check_balance()

    print("\n" + "=" * 65)
    print("  ALL WHOLESALE API TESTS (INCLUDING BATCH & SECURITY) COMPLETED!")
    print("=" * 65 + "\n")


def interactive_menu():
    while True:
        print("\n" + "=" * 48)
        print("  BShop Wholesale API Interactive Client v2.0")
        print("=" * 48)
        print("1. Check Wallet Balance & Dealer Info")
        print("2. Get Live Robux Wholesale Rate")
        print("3. Calculate Robux Price Quote")
        print("4. Check Roblox Group 15-Day Policy")
        print("5. Check Single Roblox Account Eligibility")
        print("6. Check Batch Accounts Pre-Flight Eligibility")
        print("7. View Recent Wallet Transactions")
        print("8. Place Single Robux Test Order")
        print("9. Place Batch Multi-Recipient Robux Order")
        print("10. View Order Detail by Order Number")
        print("11. Check Security Posture & Allowed IPs")
        print("12. Update Allowed IP Whitelist (IP / CIDR)")
        print("A. Run Full Automated Test Suite")
        print("0. Exit")
        print("-" * 48)

        choice = input("Select an option [0-12 or A]: ").strip().upper()
        if choice == "1":
            check_balance()
        elif choice == "2":
            get_robux_rate()
        elif choice == "3":
            amt = input("Enter Robux amount [default: 1000]: ").strip()
            quote_robux(int(amt) if amt.isdigit() else 1000)
        elif choice == "4":
            check_group_requirements()
        elif choice == "5":
            user = input("Enter Roblox username [default: builderman]: ").strip()
            check_eligibility(user or "builderman")
        elif choice == "6":
            print("Enter usernames with Robux amounts (one per line, e.g. player1: 100). Blank line to submit:")
            lines = []
            while True:
                line = input()
                if not line.strip():
                    break
                lines.append(line.strip())
            if lines:
                check_batch_eligibility(text="\n".join(lines))
            else:
                check_batch_eligibility()
        elif choice == "7":
            check_transactions(10)
        elif choice == "8":
            user = input("Enter Roblox username for delivery: ").strip()
            amt = input("Enter Robux amount (min 100): ").strip()
            if user and amt.isdigit():
                place_test_order(user, int(amt))
            else:
                print("Invalid username or Robux amount.")
        elif choice == "9":
            print("Enter recipients in format: username: amount (e.g. player1: 100). Blank line to submit:")
            lines = []
            while True:
                line = input()
                if not line.strip():
                    break
                lines.append(line.strip())
            if lines:
                payload = {
                    "recipient_text": "\n".join(lines),
                    "pay_with_balance": True,
                    "validate_eligibility": False,
                    "notes": "Manual CLI Batch Order"
                }
                res = make_request("POST", "/api/wholesale/robux/order", payload)
                print("Response:", json.dumps(res, indent=2))
            else:
                place_batch_test_order()
        elif choice == "10":
            ord_num = input("Enter order number (e.g. ORD-2026...): ").strip()
            if ord_num:
                get_order_detail(ord_num)
        elif choice == "11":
            check_security_status()
        elif choice == "12":
            print("Enter allowed IPs or CIDR subnets separated by commas (or leave blank to clear and permit all IPs):")
            raw_ips = input("Allowed IPs: ").strip()
            if raw_ips:
                ip_list = [ip.strip() for ip in raw_ips.split(",") if ip.strip()]
                update_allowed_ips(ip_list)
            else:
                update_allowed_ips([])
        elif choice in ("A", "ALL"):
            run_all_tests()
        elif choice in ("0", "EXIT", "Q"):
            print("Goodbye!")
            break
        else:
            print("Invalid choice. Please choose 0-12 or A.")


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1].lower() in ("--all", "-a", "test", "all"):
        run_all_tests()
    else:
        interactive_menu()

