#!/usr/bin/env python3
"""Mount-sicherer Speicherfix für SkyZ Files 1.0.0. Als root ausführen."""
import ast
import os
import shutil
import tempfile
from datetime import datetime
from pathlib import Path

HELPER = '''
def link_across_mounts(source, target):
    """Atomically publish without replacement, including across mount points."""
    import errno
    try:
        os.link(source, target)
        return
    except OSError as exc:
        if exc.errno != errno.EXDEV:
            raise
    target = Path(target)
    fd, name = tempfile.mkstemp(prefix='.skyz-publish-', dir=target.parent)
    staged = Path(name)
    try:
        with os.fdopen(fd, 'wb') as output, open(source, 'rb') as incoming:
            shutil.copyfileobj(incoming, output, length=1024 * 1024)
            output.flush()
            os.fchmod(output.fileno(), stat.S_IMODE(os.fstat(incoming.fileno()).st_mode) & 0o777)
            os.fsync(output.fileno())
        # Both paths now have the same parent mount. EEXIST preserves existing files.
        os.link(staged, target)
    finally:
        staged.unlink(missing_ok=True)

'''

def patch(text):
    if '\ndef link_across_mounts(' in text:
        return text
    changes = [
        ('try: os.link(tmp, target)', 'try: link_across_mounts(tmp, target)'),
        ('    os.link(temp_path, target)', '    link_across_mounts(temp_path, target)'),
    ]
    for old, new in changes:
        if text.count(old) != 1:
            raise RuntimeError('Unerwartete Codeversion; keine Datei geändert: ' + old)
        text = text.replace(old, new, 1)
    text = text.replace('folder_path.mkdir(parents=True, exist_ok=True, mode=0o2770)',
                        'folder_path.mkdir(parents=True, exist_ok=True, mode=0o770)')
    text = text.replace('source and destination share /data.', 'cross-mount copies are staged at the destination.')
    text += HELPER
    ast.parse(text)
    return text

def main():
    if os.geteuid() != 0:
        raise SystemExit('Bitte mit sudo python3 ausführen.')
    target = Path('/data/skyzfiles/app/vault/storage.py')
    if target.is_symlink() or not target.is_file():
        raise SystemExit('Erwartete reguläre Datei fehlt: ' + str(target))
    original = target.read_text()
    updated = patch(original)
    if updated == original:
        print('Speicherfix bereits vorhanden.'); return
    backup = target.with_name('storage.py.bak-' + datetime.now().strftime('%Y%m%d-%H%M%S-%f'))
    shutil.copy2(target, backup)
    fd, name = tempfile.mkstemp(prefix='.storage-fix-', dir=target.parent)
    try:
        with os.fdopen(fd, 'w') as output:
            output.write(updated); output.flush(); os.fsync(output.fileno())
        shutil.copystat(target, name)
        old_stat = target.stat()
        os.chown(name, old_stat.st_uid, old_stat.st_gid)
        os.replace(name, target)
    finally:
        Path(name).unlink(missing_ok=True)
    print('Speicherfix installiert. Sicherung:', backup)
    print('Jetzt: sudo systemctl restart skyzfiles-web skyzfiles-worker')

if __name__ == '__main__':
    main()
