"""Restart testdatadb service, rerun backfill on remaining ~438 records, verify."""
import base64, subprocess, yaml, paramiko

HOST='192.168.0.6'; USER='sysadmin'

NODE_BACKFILL = r'''
const fs = require('fs');
const path = require('path');
const db = require('./database/db');
const { loadAllSpecs, getSpecs } = require('./parsers/spec-reader');
const { generateExactDatasheet } = require('./templates/datasheet-exact');

const OUTPUT_DIR = '\\\\ad2\\webshare\\For_Web';

(async () => {
    if (!fs.existsSync(OUTPUT_DIR)) { console.error('[FAIL] output dir not reachable'); process.exit(1); }
    const specMap = loadAllSpecs();
    const where = "overall_result='PASS' AND forweb_exported_at IS NULL " +
        "AND ((model_number LIKE 'SCMVAS%' OR model_number LIKE 'SCMHVAS%' OR model_number LIKE 'VAS-M%' OR model_number LIKE 'HVAS-M%') OR log_type='VASLOG_ENG')";
    const rows = await db.query('SELECT * FROM test_records WHERE ' + where + ' ORDER BY test_date DESC');
    console.log('[INFO] ' + rows.length + ' records to process');

    let rendered = 0, passthrough = 0, skipped = 0, errors = 0;
    const batchIds = [];
    async function flush() {
        if (!batchIds.length) return;
        const now = new Date().toISOString();
        await db.transaction(async tx => {
            for (const id of batchIds) await tx.execute('UPDATE test_records SET forweb_exported_at=$1 WHERE id=$2',[now,id]);
        });
        batchIds.length = 0;
    }
    for (const r of rows) {
        try {
            const outPath = path.join(OUTPUT_DIR, r.serial_number + '.TXT');
            if (r.log_type === 'VASLOG_ENG') {
                if (r.source_file && fs.existsSync(r.source_file)) fs.copyFileSync(r.source_file, outPath);
                else fs.writeFileSync(outPath, r.raw_data || '', 'utf8');
                passthrough++;
            } else {
                const specs = getSpecs(specMap, r.model_number);
                if (!specs) { skipped++; continue; }
                const txt = generateExactDatasheet(r, specs);
                if (!txt) { skipped++; continue; }
                fs.writeFileSync(outPath, txt, 'utf8');
                rendered++;
            }
            batchIds.push(r.id);
            if (batchIds.length >= 100) { await flush(); process.stdout.write('[PROGRESS] ' + (rendered+passthrough) + '/' + rows.length + '\n'); }
        } catch (e) { errors++; console.error('[ERR] ' + r.serial_number + ': ' + e.message); }
    }
    await flush();
    console.log('\n========================================');
    console.log('Straggler Backfill Complete');
    console.log('========================================');
    console.log('Rendered:    ' + rendered);
    console.log('Passthrough: ' + passthrough);
    console.log('Skipped:     ' + skipped);
    console.log('Errors:      ' + errors);

    // Post-run count
    const remaining = await db.queryOne("SELECT COUNT(*) c FROM test_records WHERE " + where);
    console.log('Remaining backlog: ' + remaining.c);

    // Sample a plain-decimal-derived datasheet to verify render
    const sample = await db.queryOne(
        "SELECT serial_number, model_number FROM test_records WHERE forweb_exported_at IS NOT NULL " +
        "AND raw_data LIKE '%PASS .%' AND (model_number LIKE 'SCMVAS%' OR model_number LIKE 'SCMHVAS%') " +
        "ORDER BY forweb_exported_at DESC LIMIT 1"
    );
    if (sample) console.log('Plain-decimal sample just rendered: SN=' + sample.serial_number + ' model=' + sample.model_number);

    await db.close();
})().catch(e => { console.error('[FATAL] ' + e.message); process.exit(1); });
'''

def pwd():
    r = subprocess.run(['sops','-d','D:/vault/clients/dataforth/ad2.sops.yaml'],
                       capture_output=True, text=True, timeout=30, check=True)
    return yaml.safe_load(r.stdout)['credentials']['password'].replace('\\','')

def ps(c, cmd, to=1800):
    enc = base64.b64encode(cmd.encode('utf-16-le')).decode()
    stdin, stdout, stderr = c.exec_command(f'powershell -NoProfile -EncodedCommand {enc}', timeout=to)
    return stdout.read().decode('utf-8','replace'), stderr.read().decode('utf-8','replace'), stdout.channel.recv_exit_status()

c = paramiko.SSHClient()
c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect(HOST, username=USER, password=pwd(), timeout=30, banner_timeout=45, look_for_keys=False, allow_agent=False)
try:
    print('=== STEP 1: restart testdatadb ===', flush=True)
    out, err, rc = ps(c, r'Restart-Service testdatadb -Force; Start-Sleep -Seconds 3; Get-Service testdatadb | Select Name,Status | Format-Table -AutoSize | Out-String', to=60)
    print(out, flush=True)

    print('=== STEP 2: deploy and run backfill node script ===', flush=True)
    sftp = c.open_sftp()
    remote_js = 'C:/Shares/testdatadb/_backfill_stragglers.js'
    with sftp.open(remote_js, 'w') as fh: fh.write(NODE_BACKFILL)
    sftp.close()

    out, err, rc = ps(c, r'cd C:\Shares\testdatadb; & node ./_backfill_stragglers.js')
    print(f'[rc={rc}]', flush=True)
    print(out, flush=True)
    if err.strip() and 'CLIXML' not in err:
        print('--- STDERR ---', flush=True)
        print(err[:2000], flush=True)

    sftp = c.open_sftp()
    try: sftp.remove(remote_js)
    except Exception: pass
    sftp.close()
finally:
    c.close()
