"""
Diagnose CSP form-action blocking on a target page.

Usage:
    python diagnose_csp_form_action.py <login_url> <username> <password>

Produces:
    - CSP header analysis
    - Form action inspection
    - Console message capture (CSP violations)
    - Network request trace (ERR_ABORTED detection)
    - Cookie state after submission
"""
import asyncio
import sys
from playwright.async_api import async_playwright


async def diagnose(login_url: str, username: str, password: str):
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context()
        page = await context.new_page()

        console_msgs = []
        page.on("console", lambda msg: console_msgs.append(f"[{msg.type}] {msg.text}"))

        # CDP for detailed network tracing
        cdp = await context.new_cdp_session(page)
        await cdp.send("Network.enable")

        network_events = []

        def on_resp(params):
            resp = params.get("response", {})
            network_events.append({
                "type": "response",
                "status": resp.get("status"),
                "url": resp.get("url", "")[:120],
                "location": resp.get("headers", {}).get("location", ""),
            })

        def on_fail(params):
            network_events.append({
                "type": "fail",
                "error": params.get("errorText", ""),
                "reason": params.get("blockedReason", ""),
            })

        cdp.on("Network.responseReceived", on_resp)
        cdp.on("Network.loadingFailed", on_fail)

        # Step 1: Load page
        print(f"Loading: {login_url}")
        await page.goto(login_url)

        # Step 2: Check CSP
        csp = await page.evaluate("""
            async () => {
                const r = await fetch(location.href, {method: 'HEAD'});
                return r.headers.get('content-security-policy') || 'none';
            }
        """)
        has_form_action = "form-action" in csp
        print(f"CSP form-action directive: {'YES' if has_form_action else 'none'}")
        if has_form_action:
            # Extract just the form-action part
            for part in csp.split(";"):
                if "form-action" in part:
                    print(f"  → {part.strip()}")

        # Step 3: Inspect form
        form_info = await page.evaluate("""
            () => {
                const form = document.querySelector('form');
                if (!form) return null;
                return {
                    action: form.action,
                    method: form.method,
                    hasQueryInAction: form.action.includes('?'),
                    hiddenFields: Array.from(form.querySelectorAll('input[type=hidden]'))
                        .map(i => ({name: i.name, value: i.value.substring(0, 80)}))
                };
            }
        """)
        if form_info:
            print(f"\nForm action: {form_info['action']}")
            print(f"Has query in action: {form_info['hasQueryInAction']}")
            print(f"Hidden fields: {form_info['hiddenFields']}")
        else:
            print("No form found on page!")
            return

        # Step 4: Submit form
        await page.fill('input[name="username"], #username', username)
        await page.fill('input[name="password"], #password', password)

        print("\nSubmitting form...")
        try:
            async with page.expect_navigation(timeout=8000):
                await page.click('button[type="submit"], input[type="submit"]')
            print(f"Navigation OK → {page.url}")
        except Exception as e:
            print(f"Navigation FAILED: {e}")
            print(f"Stayed on: {page.url}")

        await asyncio.sleep(1)

        # Step 5: Report
        cookies = await context.cookies()
        auth_cookies = [c for c in cookies if 'auth' in c['name'].lower()]
        print(f"\nAuth cookies: {len(auth_cookies)}")
        for c in auth_cookies:
            print(f"  {c['name']} domain={c['domain']}")

        csp_errors = [m for m in console_msgs if "form-action" in m or "violates" in m]
        print(f"\nCSP violations: {len(csp_errors)}")
        for e in csp_errors:
            print(f"  {e}")

        failures = [e for e in network_events if e["type"] == "fail"]
        print(f"\nNetwork failures: {len(failures)}")
        for f in failures:
            print(f"  {f['error']} (reason: {f['reason']})")

        verdict = "BLOCKED" if csp_errors else ("OK" if "login-success" in page.url or "www." in page.url else "UNKNOWN")
        print(f"\n{'='*40}")
        print(f"VERDICT: {verdict}")

        await browser.close()


if __name__ == "__main__":
    if len(sys.argv) < 4:
        print(f"Usage: {sys.argv[0]} <login_url> <username> <password>")
        sys.exit(1)
    asyncio.run(diagnose(sys.argv[1], sys.argv[2], sys.argv[3]))
