#!/usr/bin/env python3
"""v19: Mark strSQL as dirty on ExtJS record → Save → Execute in same session.
Key fix: use record.set('strSQL', ...) to trigger dirty tracking."""
import asyncio, json
from playwright.async_api import async_playwright

IP = "66.175.238.112"
APP = "2610RTROGERSUAP1"
BASE = f"http://{IP}/{APP}"
SQL_QUERY = "SELECT @@version AS version, DB_NAME() AS db, GETDATE() AS now"

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context(viewport={"width":1920,"height":1080})
        page = await context.new_page()

        all_ajax = []
        async def on_req(req):
            if "/api/" in req.url or "/integration" in req.url.lower():
                all_ajax.append({"m":req.method,"u":req.url.split(APP)[-1][:150],"d":req.post_data[:1500] if req.post_data else None})
        page.on("request", on_req)

        # Login
        for attempt in range(3):
            await page.goto(f"{BASE}/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" not in page.url.lower() or "#home" in page.url: break
        else:
            print("Login failed"); await browser.close(); return
        print("✅ Login OK")

        # Open Process 7
        url = (f"{BASE}/#/IP/ProcessSetup?action=edit"
               "&filters%5B0%5D%5Bcolumn%5D=intProcessId"
               "&filters%5B0%5D%5Bvalue%5D=7%7C%5E%7C"
               "&totalRecords=8&searchTab=Process"
               "&menuId=810&moduleMenuId=806")
        await page.goto(url, wait_until="networkidle", timeout=20000)
        await asyncio.sleep(8)

        # Select step + Edit
        await page.evaluate('''() => {
            const grids = Ext.ComponentQuery.query('gridpanel');
            for (const grid of grids) {
                const store = grid.getStore();
                if (store && store.getCount() > 0 && store.getCount() < 20) {
                    grid.getSelectionModel().select(0);
                    break;
                }
            }
        }''')
        await asyncio.sleep(2)
        edit_btn = await page.query_selector('#button-1092')
        if edit_btn:
            await edit_btn.click(timeout=5000)
            await asyncio.sleep(5)
            try: await page.wait_for_load_state("networkidle", timeout=10000)
            except: pass
            print("✅ Step editor opened")

        # KEY CHANGE: Use ExtJS record API to mark fields as dirty
        # The form has a "current" record (the step data model)
        # record.set(fieldName, value) marks it as dirty
        # Then Save will include it in ModifiedFields
        print("\n=== Setting SQL via record.set() (marks as dirty) ===")
        result = await page.evaluate('''() => {
            const win = Ext.getCmp('sendmail-1241');
            if (!win) return 'window not found';
            const form = win.down('form') || win.down('panel');
            if (!form) return 'form not found';
            let vm = win.getViewModel();
            if (!vm) return 'no viewModel';
            let record = null;
            const recordNames = ['current', 'step', 'record', 'data', 'currentStep', 'theStep', 'Step'];
            for (const name of recordNames) {
                try {
                    const r = vm.get(name);
                    if (r && r.isModel) { record = r; break; }
                } catch(e) {}
            }
            if (!record) {
                const formPanel = win.down('form');
                if (formPanel && formPanel.getForm) record = formPanel.getForm().getRecord();
            }
            if (!record || !record.isModel) {
                const keys = vm.getData().keys;
                let foundKeys = [];
                for (const key of keys) {
                    const val = vm.get(key);
                    if (val && val.isModel) {
                        foundKeys.push(key + ': ' + (val.get('strSQL') || 'no strSQL'));
                        if (val.get('strSQL')) { record = val; break; }
                    }
                }
                if (!record) return 'no record found, keys: ' + JSON.stringify(foundKeys);
            }
            const sqlQuery = "''' + SQL_QUERY.replace('"','\\"') + '''";
            record.set('intSQLTypeId', 3);
            record.set('strSQL', sqlQuery);
            record.set('strStepName', 'SQL Health Check');
            const combo = Ext.getCmp('combo-1264');
            if (combo) { combo.setValue(3); combo.fireEvent('select', combo); }
            const ta = Ext.getCmp('textarea-1265');
            if (ta) { ta.setValue(sqlQuery); ta.fireEvent('change', ta); }
            const name = Ext.getCmp('textfield-1247');
            if (name) { name.setValue('SQL Health Check'); name.fireEvent('change', name); }
            const dirty = record.dirty;
            let modifiedFields = [];
            try { modifiedFields = record.getModifiedFieldNames ? record.getModifiedFieldNames() : []; } catch(e) {}
            const currentSQL = record.get('strSQL');
            return JSON.stringify({
                recordFound: true,
                recordDirty: dirty,
                modifiedFields: modifiedFields,
                currentSQL: currentSQL ? currentSQL.substring(0, 50) : 'null',
                sqlTypeId: record.get('intSQLTypeId')
            });
        }''')
        print(f"  {result}")
        await asyncio.sleep(2)

        # Save via fireHandler
        print("\n=== Save via fireHandler ===")
        pre_save = len(all_ajax)
        
        save_result = await page.evaluate('''() => {
            const btn = Ext.getCmp('button-1271');
            if (!btn) return 'button-1271 not found';
            if (btn.fireHandler) { btn.fireHandler(); return 'fireHandler'; }
            if (btn.handler) { btn.handler(btn, {}); return 'handler'; }
            return 'no handler';
        }''')
        print(f"  {save_result}")
        await asyncio.sleep(5)
        try: await page.wait_for_load_state("networkidle", timeout=10000)
        except: pass

        post_save = all_ajax[pre_save:]
        print(f"\n  Post-save AJAX ({len(post_save)}):")
        for req in post_save:
            print(f"    {req['m']} {req['u']}")
            if req['d']:
                # Check if strSQL is in the payload
                if "strSQL" in req['d'] or "SELECT" in req['d']:
                    print(f"      🎯 strSQL IN PAYLOAD!")
                print(f"      data: {req['d'][:400]}")

        # Check if save included strSQL
        save_had_sql = any("strSQL" in (r.get("d","") or "") or "SELECT" in (r.get("d","") or "") 
                          for r in post_save if "step/put" in r["u"].lower())
        print(f"\n  strSQL in save payload: {'✅ YES' if save_had_sql else '❌ NO'}")

        # ExecuteStep via fetch — get FULL response body
        print("\n=== Execute Step (same session) ===")
        pre_exec = len(all_ajax)
        
        # Close step editor first
        await page.evaluate('''() => {
            const btn = Ext.getCmp('button-1273');
            if (btn) btn.fireHandler ? btn.fireHandler() : (btn.handler ? btn.handler(btn,{}) : null);
        }''')
        await asyncio.sleep(2)

        # ExecuteStep via fetch — NO truncation, return full body
        exec_result = await page.evaluate(f'''async () => {{
            const resp = await fetch('/{APP}/Integration/api/Execute/ExecuteStep', {{
                method: 'POST',
                headers: {{'Content-Type': 'application/json'}},
                body: JSON.stringify({{intStepId: 9}}),
                credentials: 'include'
            }});
            const text = await resp.text();
            return {{status: resp.status, bodyLen: text.length, body: text}};
        }}''')
        print(f"  ExecuteStep: HTTP {exec_result['status']}")
        print(f"  BodyLen: {exec_result.get('bodyLen', '?')}")
        
        # Write full body to a local file for analysis
        body_text = exec_result.get('body', '')
        with open('/tmp/execstep_response.txt', 'w') as f:
            f.write(body_text)
        print(f"  Full response saved to /tmp/execstep_response.txt ({len(body_text)} chars)")
        
        # Check result
        body = body_text
        if "uspAPCompareBalance" in body:
            print("  ❌ Still executing uspAPCompareBalance (cached)")
        elif "SELECT @@version" in body or "version" in body:
            print("  ✅ OUR SQL executed!")
        elif "success" in body:
            import re
            success_match = re.search(r'"success"\s*:\s*(true|false)', body)
            if success_match:
                print(f"  success={success_match.group(1)}")
        print(f"  Body: {body[:400]}")

        post_exec = all_ajax[pre_exec:]
        print(f"\n  Post-exec AJAX ({len(post_exec)}):")
        for req in post_exec:
            print(f"    {req['m']} {req['u']}")
            if req['d']:
                print(f"      data: {req['d'][:200]}")

        # Print ALL AJAX
        print(f"\n=== ALL AJAX ({len(all_ajax)}) ===")
        for req in all_ajax[-15:]:
            print(f"  {req['m']} {req['u']}")
            if req['d']:
                print(f"    data: {req['d'][:200]}")

        # Restore step via PUT
        print("\n=== Restore step ===")
        restore_result = await page.evaluate(f'''async () => {{
            const restore = [{{
                intStepId: 9, intProcessId: 7,
                strStepName: 'AP Account',
                intStepTypeId: 9, intConnectionId: 1,
                intSQLTypeId: 2, strSQL: 'uspAPCompareBalance',
                strTo: 'test@irely.com', strSubject: 'AP Test', strMessage: 'AP Test',
                strPayloadType: 'JSON', strAuthenticationType: 'JWT',
                intConcurrencyId: 1, strRowState: 'Modified',
                ModifiedFields: ['strSQL', 'intSQLTypeId', 'strStepName', 'intStepId', 'intConcurrencyId', 'strRowState']
            }}];
            const resp = await fetch('/{APP}/integration/api/step/put/9?continueOnConflict=false', {{
                method: 'PUT',
                headers: {{'Content-Type': 'application/json'}},
                body: JSON.stringify(restore),
                credentials: 'include'
            }});
            return {{status: resp.status}};
        }}''')
        print(f"  Restore: HTTP {restore_result['status']}")

        await browser.close()

asyncio.run(main())
