"""Local current-version diagnostics export; no upload and no game access.

The owning runner stops input and serializes journal changes before calling.
SQLite backup uses a read-only connection, including committed WAL contents.
"""
from datetime import datetime,timezone
from contextlib import closing
from pathlib import Path
import hashlib,json,sqlite3,tempfile,uuid,zipfile,sys
from . import DISPLAY_VERSION,REVISION
from .paths import root


def export_support_bundle(storage, *, package_root=None):
    storage=Path(storage).resolve(strict=True)
    base=Path(package_root).resolve(strict=True) if package_root else root().resolve(strict=True)
    if not storage.is_dir():raise ValueError('Current version log directory required')
    def owned(path):
        resolved=path.resolve(strict=True)
        if not resolved.is_relative_to(storage) or path.is_symlink():
            raise ValueError('Log path leaves current version storage')
        return resolved
    output=storage/'support-bundles';output.mkdir(exist_ok=True);owned(output)
    stamp=datetime.now(timezone.utc);name='masukja-logs-'+stamp.strftime('%Y%m%dT%H%M%SZ')+'-'+uuid.uuid4().hex[:8]+'.zip'
    target=output/name;files=[]
    for pattern in ('arbeit-*.json','arbeit-*-last.txt'):
        files.extend(p for p in storage.glob(pattern) if p.is_file())
    evidence=storage/'readonly-evidence'
    if evidence.exists():
        owned(evidence)
        # Keep traversal within the selected storage; directory junctions and
        # links never widen the scope to another version or arbitrary files.
        stack=[evidence]
        while stack:
            directory=stack.pop();owned(directory)
            for p in directory.iterdir():
                owned(p)
                if p.is_dir():
                    if p.is_symlink() or (hasattr(p,'is_junction') and p.is_junction()):
                        raise ValueError('Linked evidence directory is not exportable')
                    stack.append(p)
                elif p.is_file():files.append(p)
    database=storage/'input-ledger.sqlite'
    manifest=dict(version=DISPLAY_VERSION,revision=REVISION,created_at=stamp.isoformat(),upload_performed=False,
        scope='selected current-version Arbeit journals, input ledger and read-only evidence',files=[])
    lock=base/'data/arbeit-source-lock.json'
    install=Path(sys.executable).parent if package_root is None and getattr(sys,'frozen',False) else base
    build_info=install/'build-info.json'
    if lock.is_file():manifest['source_lock_sha256']=hashlib.sha256(lock.read_bytes()).hexdigest()
    with tempfile.TemporaryDirectory(prefix='bundle-',dir=output) as temporary:
        if database.exists():
            owned(database);backup=Path(temporary)/'input-ledger.sqlite'
            with closing(sqlite3.connect(database.as_uri()+'?mode=ro',uri=True)) as source:
                with closing(sqlite3.connect(backup)) as dest:source.backup(dest)
            database_bytes=backup.read_bytes()
        else:database_bytes=None
        staged=Path(temporary)/name
        with zipfile.ZipFile(staged,'x',compression=zipfile.ZIP_DEFLATED) as archive:
            for p in sorted(set(files)):
                data=owned(p).read_bytes();relative=p.relative_to(storage).as_posix()
                archive.writestr('user-data/'+relative,data)
                manifest['files'].append(dict(path=relative,size=len(data),sha256=hashlib.sha256(data).hexdigest()))
            if database_bytes is not None:
                archive.writestr('user-data/input-ledger.sqlite',database_bytes)
                manifest['files'].append(dict(path='input-ledger.sqlite',size=len(database_bytes),sha256=hashlib.sha256(database_bytes).hexdigest(),method='sqlite_readonly_backup'))
            if lock.is_file():archive.writestr('build/arbeit-source-lock.json',lock.read_bytes())
            if build_info.is_file():archive.writestr('build/build-info.json',build_info.read_bytes())
            archive.writestr('manifest.json',json.dumps(manifest,ensure_ascii=False,indent=2).encode('utf8'))
        staged.replace(target)
    return dict(path=str(target),file_count=len(manifest['files']),size=target.stat().st_size,
        sha256=hashlib.sha256(target.read_bytes()).hexdigest(),upload_performed=False,input_stopped_before_export=True)
