"""InFinBot isolated native SQLite snapshot drill. Source opened read-only; no WAL checkpoint."""
import argparse,hashlib,json,math,sqlite3,tempfile,time
from contextlib import ExitStack,closing
from datetime import datetime,timezone
from pathlib import Path

REQUIRED_TABLES=('enterprise_portfolio','ingestion_journal','autonomous_resolution_rules','remediation_lifecycle','notification_outbox')
DEFAULT_DB='/Users/bpa/ForesixaQuant/qzotic_pure_sandbox.db'

def identifier(value):
    if not isinstance(value,str) or not value or '\x00' in value or len(value)>256:raise ValueError('Invalid table name')
    return '"'+value.replace('"','""')+'"'

def schema_and_counts(connection,required):
    schema=connection.execute("SELECT type,name,tbl_name,sql FROM sqlite_master WHERE name NOT LIKE 'sqlite_%' ORDER BY type,name").fetchall()
    tables={r[1] for r in schema if r[0]=='table'}
    missing=set(required)-tables
    if missing:raise ValueError('Required schema missing: '+', '.join(sorted(missing)))
    counts={name:connection.execute('SELECT COUNT(*) FROM '+identifier(name)).fetchone()[0] for name in sorted(required)}
    encoded=json.dumps(schema,ensure_ascii=False,separators=(',',':')).encode()
    return hashlib.sha256(encoded).hexdigest(),counts

def execute_non_destructive_backup_test(db_path=DEFAULT_DB,*,required_tables=REQUIRED_TABLES,timeout_seconds=30):
    report={'product':'InFinBot','observed_at':datetime.now(timezone.utc).isoformat(),
            'status':'BACKUP_RECOVERY_BLOCKED','source_opened_read_only':False,'source_writes_issued':False,
            'source_checkpoint_requested':False,'snapshot_reopened_read_only':False,
            'offsite_recovery_verified':False,'application_restart_verified':False,
            'independent_human_review':'NOT_PERFORMED','sqlite_version':sqlite3.sqlite_version}
    temporary=None
    try:
        if isinstance(timeout_seconds,bool) or not isinstance(timeout_seconds,(int,float)) or not math.isfinite(timeout_seconds) or not 0<timeout_seconds<=60:raise ValueError('Timeout must be 0 to 60 seconds')
        if not isinstance(required_tables,(list,tuple,set,frozenset)):raise ValueError('Required tables must be an explicit collection, not a string')
        required=tuple(required_tables)
        if not required or len(required)!=len(set(required)):raise ValueError('Explicit unique required tables needed')
        for name in required:identifier(name)
        path=Path(db_path).absolute()
        if path.is_symlink() or not path.is_file():raise ValueError('Existing regular source database required; none will be created')
        deadline=time.monotonic()+timeout_seconds
        def check_deadline():
            if time.monotonic()>=deadline:raise TimeoutError('Recovery drill deadline exceeded')
        def progress(status,remaining,total):check_deadline()
        def sql_progress():return int(time.monotonic()>=deadline)
        with ExitStack() as stack:
            temporary=stack.enter_context(tempfile.TemporaryDirectory(prefix='infinbot-native-recovery-'))
            backup=Path(temporary)/'isolated-snapshot.sqlite3'
            source=stack.enter_context(closing(sqlite3.connect(path.as_uri()+'?mode=ro',uri=True,timeout=min(1,timeout_seconds))))
            source.execute('PRAGMA query_only=ON');source.execute('PRAGMA trusted_schema=OFF')
            source.set_progress_handler(sql_progress,1000)
            report['source_opened_read_only']=True
            # Pin one logical read snapshot for schema/count reconciliation, including committed WAL pages.
            source.execute('BEGIN')
            schema,counts=schema_and_counts(source,required)
            report['source_journal_mode']=source.execute('PRAGMA journal_mode').fetchone()[0]
            report['source_snapshot_pinned_at']=datetime.now(timezone.utc).isoformat()
            destination=sqlite3.connect(backup,timeout=min(1,timeout_seconds))
            backup.chmod(0o600)
            try:
                source.backup(destination,pages=10,progress=progress,sleep=0.05)
                check_deadline()
                # Only the owned temporary destination is converted to a standalone cold file.
                mode=destination.execute('PRAGMA journal_mode=DELETE').fetchone()[0]
                if mode!='delete':raise ValueError('Temporary snapshot is not standalone')
            finally:destination.close()
            with closing(sqlite3.connect(backup.as_uri()+'?mode=ro',uri=True,timeout=1)) as recovered:
                recovered.execute('PRAGMA query_only=ON');recovered.execute('PRAGMA trusted_schema=OFF')
                recovered.set_progress_handler(sql_progress,1000)
                report['snapshot_reopened_read_only']=True
                integrity=[r[0] for r in recovered.execute('PRAGMA integrity_check')]
                if integrity!=['ok']:raise ValueError('Recovered snapshot integrity failed')
                if recovered.execute('PRAGMA foreign_key_check').fetchone() is not None:raise ValueError('Recovered snapshot foreign-key violations')
                recovered_schema,recovered_counts=schema_and_counts(recovered,required)
                if schema!=recovered_schema or counts!=recovered_counts:raise ValueError('Pinned source/snapshot reconciliation mismatch')
                report.update(status='NATIVE_SNAPSHOT_REOPEN_AND_SCHEMA_COUNTS_VERIFIED',integrity_check='ok',foreign_key_check='clear',
                              required_table_count=len(required),required_table_row_counts=recovered_counts,
                              schema_sha256=recovered_schema,snapshot_size_bytes=backup.stat().st_size)
            source.rollback()
        report['temporary_workspace_removed']=not Path(temporary).exists()
        if not report['temporary_workspace_removed']:raise ValueError('Owned temporary cleanup incomplete')
    except Exception as error:
        report['status']='BACKUP_RECOVERY_BLOCKED'
        report['reason']=str(error) if isinstance(error,(ValueError,TimeoutError)) else 'SQLite or filesystem validation failed; source preserved'
        if temporary is not None:report['temporary_workspace_removed']=not Path(temporary).exists()
    report['scope']='Native snapshot integrity, declared schema and row counts at one pinned source read snapshot; not application/offsite recovery'
    return report

def main():
    p=argparse.ArgumentParser(description=__doc__);p.add_argument('--db-path',default=DEFAULT_DB)
    p.add_argument('--required-table',action='append');p.add_argument('--timeout-seconds',type=float,default=30)
    args=p.parse_args();report=execute_non_destructive_backup_test(args.db_path,required_tables=args.required_table or REQUIRED_TABLES,timeout_seconds=args.timeout_seconds)
    print(json.dumps(report,indent=2,allow_nan=False))
    return int(report['status']!='NATIVE_SNAPSHOT_REOPEN_AND_SCHEMA_COUNTS_VERIFIED')

if __name__=='__main__':raise SystemExit(main())
