"""Phase 4 browser check: campaign wizard (validation, audience counts, personalised preview), sending through
the queue, the live report (delivery funnel, failure reasons, recipient filters), scheduling, editing, pausing,
cancelling, duplicating and deleting. Run against a freshly seeded DB with WHATSAPP_PROVIDER=fake:
    python e2e/e2e_campaigns.py ./e2e-shots
"""
import os, re, sys, datetime
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")
HDR = {"X-Requested-With": "devwebs"}
errors = []

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

with sync_playwright() as p:
    b = p.chromium.launch()
    ctx = b.new_context(viewport={"width": 1440, "height": 900})
    page = ctx.new_page()
    page.on("pageerror", lambda e: errors.append(f"pageerror: {e}"))
    page.on("console", lambda m: m.type == "error" and "Failed to load resource" not in m.text and errors.append(f"console: {m.text}"))
    page.goto(f"{BASE}/login")
    page.fill("#email", "owner@acme.test"); page.fill("#password", "ChangeMe!2026"); page.click("button[type=submit]")
    page.wait_for_url("**/dashboard")
    api = ctx.request

    # ---- Setup through the API: number, templates, a group with a mix of consent states ----
    acc = api.post(f"{BASE}/api/whatsapp/accounts", headers=HDR, data={"waba_id": "102290129340398", "phone_number_id": "106540352242922", "access_token": "EAAG" + "x" * 60 + "WXYZ", "app_secret": "a" * 32}).json()["data"]
    api.post(f"{BASE}/api/templates/sync", headers=HDR, data={})
    promo = api.post(f"{BASE}/api/templates", headers=HDR, data={"name": "diwali_sale", "language": "en_US", "category": "MARKETING", "body": {"text": "Hi {{1}}, 20% off everything this Diwali!", "examples": {"1": "Asha"}}}).json()["data"]
    api.post(f"{BASE}/api/templates/{promo['id']}/simulate-review", headers=HDR, data={"status": "APPROVED"})
    api.post(f"{BASE}/api/contacts", headers=HDR, data={"first_name": "Faisal", "last_name": "Khan", "phone": "+919800000000", "opt_in_status": "opted_in", "consent_source": "web form"})
    contacts = api.get(f"{BASE}/api/contacts?per_page=100").json()["data"]
    groups = api.get(f"{BASE}/api/groups").json()["data"]
    vip = next(g for g in groups if g["name"] == "VIP Customers")
    r = api.post(f"{BASE}/api/contacts/bulk", headers=HDR, data={"action": "add_to_group", "ids": [c["id"] for c in contacts], "group_id": vip["id"]})
    assert r.ok, r.text()

    # ---- Dashboard step points at campaigns; nav item is live ----
    page.reload()
    expect(page.get_by_role("link", name="Create a campaign")).to_be_visible()
    nav = page.get_by_role("navigation", name="Main")
    nav.get_by_role("link", name="Campaigns").click()
    expect(page.get_by_text("No campaigns yet")).to_be_visible()
    shot(page, "c01-empty")
    page.get_by_role("button", name="Create your first campaign").click()
    page.wait_for_url("**/campaigns/new")

    # ---- Wizard ----
    page.get_by_role("button", name="Continue").click()
    expect(page.get_by_text("Give the campaign a name")).to_be_visible()
    page.fill("#cw-title", "Diwali offer")
    page.get_by_role("button", name="Continue").click()
    expect(page.get_by_text("Who should receive it?")).to_be_visible()
    page.get_by_role("button", name="Continue").click()
    expect(page.get_by_text("Choose at least one group, or all contacts")).to_be_visible()
    page.get_by_text("VIP Customers (6)").click()
    shot(page, "c02-audience")
    page.get_by_role("button", name="Continue").click()
    page.locator("#cw-template").select_option(label="diwali_sale (en_US, marketing)")
    expect(page.get_by_text("Marketing template: never sent to opted-out contacts.")).to_be_visible()
    expect(page.get_by_label("{{1}} fallback")).to_have_value("there")
    shot(page, "c03-message")
    page.get_by_role("button", name="Continue").click()
    expect(page.get_by_text("When should it go out?")).to_be_visible()
    page.get_by_role("button", name="Continue").click()
    # 6 in the group: Kanchan opted out, Piper never opted in -> 4 receive it.
    expect(page.get_by_text("of 6 targeted contacts will receive this message")).to_be_visible()
    expect(page.locator("span.tabular", has_text="4").first).to_be_visible()
    expect(page.get_by_text("Excluded: opted out of marketing")).to_be_visible()
    expect(page.get_by_text("Excluded: no marketing opt-in")).to_be_visible()
    expect(page.get_by_text("20% off everything this Diwali!")).to_be_visible()
    shot(page, "c04-review")
    page.get_by_role("button", name="Launch campaign").click()
    page.wait_for_url("**/campaigns/*")
    expect(page.get_by_role("heading", name="Diwali offer")).to_be_visible()

    # ---- Report: the queue sends within seconds; one number is undeliverable ----
    expect(page.locator("span", has_text=re.compile(r"^Completed$")).filter(visible=True)).to_be_visible(timeout=20000)
    page.reload()
    expect(page.get_by_text("Why some failed")).to_be_visible()
    expect(page.get_by_text("Why some were skipped")).to_be_visible()
    camp_id = int(page.url.rstrip("/").split("/")[-1])
    msgs = api.get(f"{BASE}/api/whatsapp/accounts/{acc['id']}/messages").json()["data"]
    out = [m for m in msgs if m["direction"] == "out" and m["status"] != "failed"]
    assert len(out) == 3, msgs
    for m in out[:2]:
        api.post(f"{BASE}/api/whatsapp/accounts/{acc['id']}/simulate", headers=HDR, data={"kind": "status", "message_id": m["id"], "status": "delivered"})
    api.post(f"{BASE}/api/whatsapp/accounts/{acc['id']}/simulate", headers=HDR, data={"kind": "status", "message_id": out[0]["id"], "status": "read"})
    page.reload()
    stats = api.get(f"{BASE}/api/campaigns/{camp_id}").json()["data"]["stats"]
    assert stats == {"total": 6, "pending": 0, "skipped": 2, "sent": 3, "delivered": 2, "read": 1, "failed": 1}, stats
    expect(page.get_by_text("50% of delivered")).to_be_visible()
    expect(page.get_by_text("Rohan Mehta")).to_be_visible()
    shot(page, "c05-report")
    tabs = page.get_by_role("tablist", name="Filter recipients")
    tabs.get_by_role("tab", name="Failed").click()
    expect(page.get_by_text("Faisal Khan")).to_be_visible()
    expect(page.get_by_role("cell", name="Rohan Mehta")).to_have_count(0)
    tabs.get_by_role("tab", name="Skipped").click()
    expect(page.get_by_text("Kanchan Kale")).to_be_visible()
    tabs.get_by_role("tab", name="All").click()
    page.get_by_label("Search recipients").fill("rohan")
    expect(page.get_by_text("Celeste Elliott")).to_have_count(0)
    expect(page.get_by_text("Rohan Mehta")).to_be_visible()
    with page.expect_download() as dl:
        page.get_by_role("button", name="Export").click()
    assert dl.value.suggested_filename.endswith(".xlsx")

    # ---- Duplicate -> edit the draft -> schedule for later ----
    page.get_by_role("button", name="Duplicate").click()
    page.wait_for_url("**/edit")
    expect(page.locator("#cw-title")).to_have_value("Diwali offer (copy)")
    page.fill("#cw-title", "Diwali reminder")
    page.get_by_role("navigation", name="Campaign steps").get_by_role("button", name=re.compile(r"Schedule$")).click()
    page.get_by_text("Pick a date and time").click()
    page.fill("#cw-at", "2020-01-01T10:00")
    page.get_by_role("button", name="Continue").click()
    page.get_by_role("button", name="Schedule campaign").click()
    expect(page.locator("#cw-at")).to_be_visible()  # sent back to the schedule step with the error
    expect(page.get_by_text("in the future", exact=False)).to_be_visible()
    shot(page, "c06-schedule-error")
    page.fill("#cw-at", (datetime.date.today() + datetime.timedelta(days=30)).isoformat() + "T09:30")
    page.locator("#cw-tz").select_option("Asia/Kolkata")
    page.get_by_role("button", name="Continue").click()
    page.get_by_role("button", name="Schedule campaign").click()
    page.wait_for_url("**/campaigns/*")
    expect(page.locator("span", has_text=re.compile(r"^Scheduled$")).filter(visible=True)).to_be_visible()
    expect(page.get_by_text(re.compile(r"9:30 am \(Asia/Kolkata\)")).filter(visible=True)).to_be_visible()
    shot(page, "c07-scheduled")
    page.get_by_role("button", name="Cancel campaign").click()
    page.locator("dialog[open]").get_by_role("button", name="Cancel campaign").click()
    expect(page.locator("span", has_text=re.compile(r"^Cancelled$")).filter(visible=True)).to_be_visible()
    page.get_by_role("button", name="Delete campaign").click()
    page.locator("dialog[open]").get_by_role("button", name="Delete").click()
    page.wait_for_url("**/campaigns")

    # ---- Draft save, and pause/resume on a sending campaign ----
    page.get_by_role("button", name="New campaign").click()
    page.fill("#cw-title", "Hello all")
    page.get_by_role("button", name="Continue").click()
    page.get_by_text("All contacts", exact=True).click()
    page.get_by_role("button", name="Continue").click()
    page.locator("#cw-template").select_option(label="hello_world (en_US, utility)")
    page.get_by_role("button", name="Save draft").click()
    page.wait_for_url("**/campaigns")
    expect(page.get_by_role("link", name="Hello all")).to_be_visible()
    expect(page.locator("span", has_text=re.compile(r"^Draft$")).filter(visible=True)).to_be_visible()
    shot(page, "c08-list")
    page.get_by_role("tab", name="Drafts").click()
    expect(page.get_by_role("link", name="Diwali offer")).to_have_count(0)
    page.get_by_role("tab", name="All").click()
    page.get_by_role("link", name="Hello all").click()
    page.wait_for_url("**/edit")
    page.get_by_role("navigation", name="Campaign steps").get_by_role("button", name=re.compile(r"Review$")).click()
    # Utility template: consent filter doesn't apply, only the opted-out contact is excluded.
    expect(page.get_by_text("of 6 targeted contacts will receive this message")).to_be_visible()
    page.get_by_role("button", name="Launch campaign").click()
    page.wait_for_url(lambda u: u.endswith("/edit") is False and "/campaigns/" in u)
    expect(page.locator("span", has_text=re.compile(r"^Completed$")).filter(visible=True)).to_be_visible(timeout=20000)

    # ---- Mobile ----
    m = b.new_context(viewport={"width": 390, "height": 844}, storage_state=ctx.storage_state())
    mp = m.new_page()
    mp.on("pageerror", lambda e: errors.append(f"mobile pageerror: {e}"))
    mp.goto(f"{BASE}/campaigns/{camp_id}")
    expect(mp.get_by_role("heading", name="Diwali offer")).to_be_visible()
    assert mp.evaluate("document.documentElement.scrollWidth <= window.innerWidth"), "report overflows on mobile"
    shot(mp, "c09-mobile-report")
    mp.goto(f"{BASE}/campaigns/new")
    expect(mp.locator("#cw-title")).to_be_visible()
    assert mp.evaluate("document.documentElement.scrollWidth <= window.innerWidth"), "wizard overflows on mobile"
    shot(mp, "c10-mobile-wizard")
    mp.goto(f"{BASE}/campaigns")
    assert mp.evaluate("document.documentElement.scrollWidth <= window.innerWidth"), "list overflows on mobile"

    b.close()

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