"""Phase 7 browser check: wallet recharge through the (simulated) Razorpay checkout with server verification, GST
invoice, declined payment, plan change paid online, notifications; platform admin (overview, customers, refund,
wallet adjustment, plans, pricing, settings, reconciliation, audit); reseller portal (plan prices within markup,
customer onboarding, earnings); a reseller's customer on trial; mobile layouts.
Run against a freshly seeded DB with WHATSAPP_PROVIDER=fake and PAYMENTS_PROVIDER=fake (the defaults outside production):
    python e2e/e2e_billing.py ./e2e-shots
"""
import os, re, sys
from playwright.sync_api import sync_playwright, expect

OUT = sys.argv[1] if len(sys.argv) > 1 else "./e2e-shots"
os.makedirs(OUT, exist_ok=True)
BASE = os.environ.get("E2E_BASE", "http://localhost:3000")
errors = []

def shot(page, name, full=False):
    page.screenshot(path=f"{OUT}/{name}.png", full_page=full)

def login(browser, email, viewport=(1440, 900)):
    ctx = browser.new_context(viewport={"width": viewport[0], "height": viewport[1]})
    page = ctx.new_page()
    page.on("pageerror", lambda e: errors.append(f"{email} pageerror: {e}"))
    page.on("console", lambda m: m.type == "error" and "Failed to load resource" not in m.text and errors.append(f"{email} console: {m.text}"))
    page.goto(f"{BASE}/login")
    page.fill("#email", email); page.fill("#password", "ChangeMe!2026"); page.click("button[type=submit]")
    page.wait_for_url(re.compile(r"/(dashboard|reseller)"))
    return ctx, page

with sync_playwright() as p:
    b = p.chromium.launch()

    # ---- Customer owner: wallet, invoice, plan ----
    ctx, page = login(b, "owner@acme.test")
    expect(page.get_by_role("link", name="Set up bot replies")).to_be_visible()
    page.get_by_role("navigation", name="Main").get_by_role("link", name="Wallet & plan").click()
    expect(page.get_by_text("₹500.00", exact=True)).to_be_visible()
    expect(page.get_by_text("Test mode")).to_be_visible()
    expect(page.get_by_text(re.compile(r"^Growth")).first).to_be_visible()
    page.get_by_role("button", name="Recharge").click()
    dlg = page.locator("dialog[open]")
    dlg.get_by_role("button", name="₹1,000", exact=True).click()
    expect(dlg.locator("#rc-amount")).to_have_value("1000")
    dlg.get_by_role("button", name="Continue to payment").click()
    dlg = page.locator("dialog[open]")
    expect(dlg.get_by_text("Test payment")).to_be_visible()
    expect(dlg.get_by_text("₹180.00")).to_be_visible()
    shot(page, "w01-test-checkout")
    dlg.get_by_role("button", name="Pay ₹1,180.00").click()
    expect(page.get_by_text("₹1,000.00 added to your wallet")).to_be_visible()
    expect(page.get_by_text("₹1,500.00", exact=True)).to_be_visible()

    # A declined payment changes nothing.
    page.get_by_role("button", name="Recharge").click()
    page.locator("dialog[open]").get_by_role("button", name="Continue to payment").click()
    page.locator("dialog[open]").get_by_role("button", name="Simulate a decline").click()
    expect(page.get_by_text("Payment failed: Payment was declined by the bank (simulated)")).to_be_visible()
    expect(page.get_by_text("₹1,500.00", exact=True)).to_be_visible()

    tabs = page.get_by_role("tablist", name="Billing sections")
    tabs.get_by_role("tab", name="Transactions").click()
    expect(page.get_by_text(re.compile("Recharge via Razorpay"))).to_be_visible()
    tabs.get_by_role("tab", name="Payments & invoices").click()
    expect(page.get_by_text("Failed", exact=True)).to_be_visible()
    with ctx.expect_page() as inv:
        page.get_by_role("link", name="View").first.click()
    invoice = inv.value
    expect(invoice.get_by_text("Tax invoice")).to_be_visible()
    expect(invoice.get_by_text("₹1,180.00")).to_be_visible()
    shot(invoice, "w02-invoice")
    invoice.close()
    tabs.get_by_role("tab", name="Message prices").click()
    expect(page.get_by_text("+91").first).to_be_visible()
    tabs.get_by_role("tab", name="Billing details").click()
    page.fill("#bp-name", "Acme Retail Pvt Ltd")
    page.fill("#bp-gstin", "27ABCDE1234F1Z5")
    page.fill("#bp-state", "29")
    page.get_by_role("button", name="Save details").click()
    expect(page.get_by_text("Your GSTIN is registered in state 27")).to_be_visible()
    page.fill("#bp-state", "27")
    page.get_by_role("button", name="Save details").click()
    expect(page.get_by_text("Billing details saved")).to_be_visible()

    page.get_by_role("button", name="Change plan").click()
    dlg = page.locator("dialog[open]")
    business = dlg.locator("div.rounded-xl").filter(has_text="Business")
    expect(business.get_by_role("button", name="Pay from wallet")).to_be_disabled()
    shot(page, "w03-plans")
    business.get_by_role("button", name="Pay online").click()
    page.locator("dialog[open]").get_by_role("button", name=re.compile(r"^Pay ₹9,438")).click()
    expect(page.get_by_text("Plan payment received")).to_be_visible()
    expect(page.get_by_text("₹7,999 per month", exact=False).first).to_be_visible()
    tabs.get_by_role("tab", name="Usage").click()
    shot(page, "w04-wallet-page", full=True)
    page.get_by_role("button", name=re.compile("^Notifications")).click()
    expect(page.get_by_text("Wallet recharged").first).to_be_visible()
    expect(page.get_by_text("Payment failed").first).to_be_visible()
    page.keyboard.press("Escape")

    # ---- Platform admin ----
    actx, admin = login(b, "admin@devwebs.test")
    nav = admin.get_by_role("navigation", name="Main")
    nav.get_by_role("link", name="Platform overview").click()
    expect(admin.get_by_text("Payments run in test mode")).to_be_visible()
    expect(admin.get_by_text("Wallet recharges")).to_be_visible()
    shot(admin, "w05-admin-overview")
    nav.get_by_role("link", name="Customers").click()
    expect(admin.get_by_role("link", name="Bluebell Cafe")).to_be_visible()
    expect(admin.get_by_role("cell", name="Nexa Digital")).to_be_visible()
    admin.get_by_role("link", name="Acme Retail").click()
    expect(admin.get_by_role("heading", name=re.compile("Acme Retail"))).to_be_visible()
    shot(admin, "w06-admin-customer")
    admin.locator("li", has_text=re.compile("Wallet recharge ₹1,180.00")).get_by_role("button", name="Refund").click()
    dlg = admin.locator("dialog[open]")
    dlg.locator("#rf-amount").fill("590")
    expect(dlg.get_by_text("₹500.00 will be taken out of the customer's wallet")).to_be_visible()
    dlg.locator("#rf-reason").fill("Customer requested a partial refund")
    dlg.get_by_role("button", name="Refund ₹590.00").click()
    expect(admin.get_by_text("Refund of ₹590.00 started")).to_be_visible()
    admin.get_by_role("button", name="Adjust wallet").click()
    dlg = admin.locator("dialog[open]")
    dlg.locator("#aw-amount").fill("100")
    dlg.locator("#aw-reason").fill("Goodwill credit for the delay")
    dlg.get_by_role("button", name="Add credit").click()
    expect(admin.get_by_text("Wallet adjusted")).to_be_visible()
    expect(admin.get_by_text("Adjustment by Devwebs: Goodwill credit for the delay")).to_be_visible()

    nav.get_by_role("link", name="Plans").click()
    admin.get_by_role("button", name="New plan").click()
    dlg = admin.locator("dialog[open]")
    dlg.locator("#pl-name").fill("Enterprise")
    dlg.locator("#pl-code").fill("enterprise")
    dlg.locator("#pl-price").fill("19999")
    dlg.get_by_label("Bot flows").check()
    dlg.get_by_role("button", name="Create plan").click()
    expect(admin.get_by_text("Plan created")).to_be_visible()
    expect(admin.get_by_role("heading", name="Enterprise")).to_be_visible()
    nav.get_by_role("link", name="Message pricing").click()
    expect(admin.get_by_text("₹0.8631")).to_be_visible()
    shot(admin, "w07-admin-pricing")
    nav.get_by_role("link", name="Billing settings").click()
    expect(admin.get_by_text("Test simulation (PAYMENTS_PROVIDER=fake)")).to_be_visible()
    expect(admin.get_by_text(re.compile(r"/api/webhooks/razorpay$"))).to_be_visible()
    nav.get_by_role("link", name="Reconciliation").click()
    expect(admin.get_by_text("Wallets match their ledgers")).to_be_visible()
    expect(admin.get_by_text("Every balance equals the sum of its ledger entries.")).to_be_visible()
    shot(admin, "w08-reconciliation")
    nav.get_by_role("link", name="Audit log").click()
    expect(admin.get_by_text("platform.payment_refunded")).to_be_visible()
    nav.get_by_role("link", name="Resellers").click()
    expect(admin.get_by_role("link", name="Nexa Digital")).to_be_visible()

    # ---- Reseller portal ----
    rctx, res = login(b, "reseller@nexa.test")
    res.wait_for_url("**/reseller")
    expect(res.get_by_role("heading", name="Reseller overview")).to_be_visible()
    rnav = res.get_by_role("navigation", name="Main")
    expect(rnav.get_by_role("link", name="Inbox")).to_have_count(0)
    rnav.get_by_role("link", name="Plan prices").click()
    res.get_by_label("Growth price").fill("4999")
    res.locator("tr", has_text="Growth").get_by_role("button", name="Save").click()
    expect(res.get_by_text(re.compile("Set a price between ₹2,999.00 and ₹3,748.75"))).to_be_visible()
    res.get_by_label("Growth price").fill("3499")
    res.locator("tr", has_text="Growth").get_by_role("button", name="Save").click()
    expect(res.get_by_text("Growth price saved")).to_be_visible()
    rnav.get_by_role("link", name="Customers").click()
    expect(res.get_by_role("link", name="Bluebell Cafe")).to_be_visible()
    res.get_by_role("button", name="New customer").click()
    dlg = res.locator("dialog[open]")
    dlg.locator("#td-name").fill("Lotus Salon")
    dlg.locator("#td-owner").fill("Lata Iyer")
    dlg.locator("#td-email").fill("lata@lotus.test")
    dlg.locator("#td-pw").fill("ChangeMe!2026")
    growth = dlg.locator("#td-plan option", has_text="Growth").first.get_attribute("value")
    dlg.locator("#td-plan").select_option(growth)
    dlg.get_by_role("button", name="Create customer").click()
    res.wait_for_url(re.compile(r"/reseller/customers/\d+$"))
    expect(res.get_by_role("heading", name=re.compile("Lotus Salon"))).to_be_visible()
    shot(res, "w09-reseller-customer")
    rnav.get_by_role("link", name="Earnings").click()
    expect(res.get_by_text("Commission ledger")).to_be_visible()
    # Resellers can't reach the platform admin.
    res.goto(f"{BASE}/admin")
    expect(res.get_by_text(re.compile("permission", re.I))).to_be_visible()

    # ---- The reseller's customer is on a trial ----
    cctx, cafe = login(b, "cafe@bluebell.test")
    cafe.goto(f"{BASE}/billing")
    expect(cafe.get_by_text("Trial", exact=True)).to_be_visible()
    expect(cafe.get_by_text(re.compile(r"^Starter")).first).to_be_visible()

    # ---- Mobile ----
    mctx = b.new_context(viewport={"width": 390, "height": 844}, storage_state=ctx.storage_state())
    mp = mctx.new_page()
    mp.on("pageerror", lambda e: errors.append(f"mobile pageerror: {e}"))
    mp.goto(f"{BASE}/billing")
    expect(mp.get_by_text("Wallet balance")).to_be_visible()
    assert mp.evaluate("document.documentElement.scrollWidth <= window.innerWidth"), "billing overflows on mobile"
    shot(mp, "w10-mobile-billing", full=True)
    amctx = b.new_context(viewport={"width": 390, "height": 844}, storage_state=actx.storage_state())
    amp = amctx.new_page()
    for path in ["/admin", "/admin/customers", "/admin/payments", "/admin/reconciliation"]:
        amp.goto(f"{BASE}{path}")
        amp.wait_for_timeout(700)
        assert amp.evaluate("document.documentElement.scrollWidth <= window.innerWidth"), f"{path} overflows on mobile"
    b.close()

if errors:
    print("\n".join(errors)); sys.exit(1)
print("billing e2e passed")
