"""Backfill SCMVAS/SCMHVAS datasheets to \\ad2\webshare\For_Web.

Deploys a one-off node script that:
  - Queries PASS records with NULL forweb_exported_at AND (SCMVAS/SCMHVAS/VAS-M/HVAS-M
    model OR log_type=VASLOG_ENG)
  - For VASLOG_ENG: copies source .txt verbatim to For_Web\<SN>.TXT (pass-through)
  - For VASLOG SCMVAS/SCMHVAS: runs generateExactDatasheet and writes
  - Updates forweb_exported_at per batch

Runs in --dry-run mode by default; pass --go to actually write. Also supports
--limit N to cap.
"""
import argparse, base64, subprocess, sys, yaml, paramiko

HOST='192.168.0.6'; USER='sysadmin'

NODE_SCRIPT = 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 function main() {
    const args = process.argv.slice(2);
    const dry = args.includes('--dry-run');
    const limitIdx = args.indexOf('--limit');
    const limit = limitIdx >= 0 ? parseInt(args[limitIdx + 1], 10) : 0;

    console.log('[INFO] output: ' + OUTPUT_DIR);
    console.log('[INFO] dry-run: ' + dry);
    console.log('[INFO] limit: ' + (limit || 'none'));

    if (!fs.existsSync(OUTPUT_DIR)) {
        console.error('[FAIL] output dir not reachable: ' + OUTPUT_DIR);
        process.exit(1);
    }

    console.log('[INFO] loading specs...');
    const specMap = loadAllSpecs();

    const where = [
        "overall_result = 'PASS'",
        "forweb_exported_at IS NULL",
        "((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')"
    ].join(' AND ');
    let sql = 'SELECT * FROM test_records WHERE ' + where + ' ORDER BY test_date DESC';
    if (limit > 0) sql += ' LIMIT ' + limit;

    const rows = await db.query(sql);
    console.log('[INFO] ' + rows.length + ' records to process');

    let exported = 0;
    let skipped = 0;
    let errors = 0;
    let passthrough = 0;
    let rendered = 0;
    const batchIds = [];
    const BATCH = 200;

    async function flush() {
        if (batchIds.length === 0) return;
        if (dry) { batchIds.length = 0; 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 (let i = 0; i < rows.length; i++) {
        const record = rows[i];
        try {
            const outPath = path.join(OUTPUT_DIR, record.serial_number + '.TXT');

            if (record.log_type === 'VASLOG_ENG') {
                if (record.source_file && fs.existsSync(record.source_file)) {
                    if (!dry) fs.copyFileSync(record.source_file, outPath);
                    passthrough++;
                } else {
                    if (!dry) fs.writeFileSync(outPath, record.raw_data || '', 'utf8');
                    passthrough++;
                }
            } else {
                const specs = getSpecs(specMap, record.model_number);
                if (!specs) { skipped++; continue; }
                const txt = generateExactDatasheet(record, specs);
                if (!txt) { skipped++; continue; }
                if (!dry) fs.writeFileSync(outPath, txt, 'utf8');
                rendered++;
            }

            batchIds.push(record.id);
            exported++;

            if (batchIds.length >= BATCH) {
                await flush();
                process.stdout.write('[PROGRESS] ' + exported + '/' + rows.length + '\n');
            }
        } catch (e) {
            errors++;
            console.error('[ERR] ' + record.serial_number + ': ' + e.message);
        }
    }
    await flush();

    console.log('');
    console.log('========================================');
    console.log('Backfill Complete' + (dry ? ' (DRY RUN)' : ''));
    console.log('========================================');
    console.log('Processed:     ' + exported);
    console.log('  rendered:    ' + rendered);
    console.log('  passthrough: ' + passthrough);
    console.log('Skipped:       ' + skipped);
    console.log('Errors:        ' + errors);
    await db.close();
}

main().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=7200):
    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()

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument('--go', action='store_true', help='actually write (default is dry-run)')
    ap.add_argument('--limit', type=int, default=0, help='cap records processed')
    args = ap.parse_args()
    dry = not args.go

    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:
        sftp = c.open_sftp()
        remote = 'C:/Shares/testdatadb/_backfill_scmvas.js'
        with sftp.open(remote,'w') as fh:
            fh.write(NODE_SCRIPT)
        sftp.close()
        print(f'[OK] deployed {remote}', flush=True)

        flags = ['--dry-run'] if dry else []
        if args.limit > 0:
            flags += ['--limit', str(args.limit)]
        cmd = r'cd C:\Shares\testdatadb; & node ./_backfill_scmvas.js ' + ' '.join(flags)
        print(f'[RUN] {cmd}', flush=True)
        out, err, rc = ps(c, cmd)
        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)
        except Exception: pass
        sftp.close()
    finally:
        c.close()

if __name__ == '__main__':
    main()
