"""Persistent remote-si settings. Root browsing stays available; indexing is bounded."""
import copy
import hashlib
import json
import os
from pathlib import Path
import time

import field_profiles as profiles
from field_ui import print, input

SETTINGS=Path(profiles.account().pw_dir if profiles.server.CURRENT else '/home/si')/'.vscode-server/data/Machine/settings.json'
BLOCKS=('**/proc/**','**/sys/**','**/dev/**','**/backup/**','**/training/**','**/storage/**',
        '**/contextra/data/**','**/.vscode-server/**','**/node_modules/**','**/.git/**')
RECOMMENDED={
    'npm.autoDetect':'off', 'npm.enableScriptExplorer':False, 'npm.enableRunFromFolder':False,
    'search.followSymlinks':False, 'git.autoRepositoryDetection':'openEditors',
    'python.analysis.languageServerMode':'light', 'python.analysis.indexing':False,
    'python.analysis.diagnosticMode':'openFilesOnly', 'python.analysis.exclude':['**'],
    'python.analysis.autoSearchPaths':False, 'python.analysis.userFileIndexFollowSymlinkedFolders':False,
    'python-envs.workspaceSearchPaths':[],
    'search.exclude':{p:True for p in BLOCKS}, 'files.watcherExclude':{p:True for p in BLOCKS},
}


def plan(current):
    result=copy.deepcopy(current);changes=[]
    for key,wanted in RECOMMENDED.items():
        if isinstance(wanted,dict):
            before=result.setdefault(key,{})
            if not isinstance(before,dict):raise ValueError('Invalid setting object: '+key)
            for child,value in wanted.items():
                if before.get(child)!=value:
                    changes.append({'key':key,'child':child,'present':child in before,'before':before.get(child),'after':value})
                    before[child]=value
        elif result.get(key)!=wanted:
            changes.append({'key':key,'present':key in result,'before':result.get(key),'after':wanted})
            result[key]=copy.deepcopy(wanted)
    return result,changes


def load():
    profiles.safe_path(SETTINGS)
    if not SETTINGS.exists():return {},None
    raw=SETTINGS.read_bytes()
    if len(raw)>1024**2:raise ValueError('Settings too large; preserve for manual review')
    try:doc=json.loads(raw)
    except ValueError:raise ValueError('Settings contain JSONC/invalid JSON. Preserve and merge reviewed settings manually; no overwrite.') from None
    if not isinstance(doc,dict):raise ValueError('Settings object required')
    return doc,raw


def apply():
    current,raw=load();desired,changes=plan(current)
    if not changes:print('Remote VSCode safety settings already match.');return
    if os.geteuid()!=0:raise ValueError('sudo required')
    backup=profiles.STATE/'vscode-safety'/str(time.time_ns())
    backup.mkdir(parents=True,mode=0o700)
    if raw is not None:profiles.atomic(backup/'before.json',raw)
    # The original may contain unrelated private settings; it is never exported or printed.
    if (SETTINGS.read_bytes() if SETTINGS.exists() else None)!=raw:raise ValueError('Settings changed during planning')
    profiles.atomic(backup/'changes.json',{'changes':changes,'target':str(SETTINGS)})
    profiles.atomic(SETTINGS,desired,user=True)
    profiles.atomic(profiles.STATE/'vscode-safety-last.json',{'backup':str(backup)})
    print('Remote si settings saved:',SETTINGS,'private rollback:',backup)
    print('Root browsing preserved; automatic npm scan disabled; Pylance light/open-files only.')
    print('No extension removed or process restarted. Existing workspace settings may override remote defaults.')


def restore():
    record=profiles.read_json(profiles.STATE/'vscode-safety-last.json')
    backup=profiles.safe_path(record['backup'])
    if backup.parent!=profiles.STATE/'vscode-safety':raise ValueError('Invalid settings backup')
    data=profiles.read_json(backup/'changes.json');current,raw=load()
    for c in data['changes']:
        values=current[c['key']] if 'child' in c else current;key=c.get('child',c['key'])
        if values.get(key)!=c['after']:raise ValueError('Setting changed since apply; preserve user edits: '+c['key'])
    for c in data['changes']:
        values=current[c['key']] if 'child' in c else current;key=c.get('child',c['key'])
        if c['present']:values[key]=c['before']
        else:values.pop(key,None)
    if SETTINGS.read_bytes()!=raw:raise ValueError('Settings changed during restore')
    profiles.atomic(SETTINGS,current,user=True)
    print('Restored only managed keys. Unrelated settings preserved. No process restarted.')


def main(args):
    action=args[0] if args else 'status'
    if len(args)>1:raise ValueError('ct vscode status | apply | restore')
    if action=='status':
        current,_=load();_,changes=plan(current)
        print('Remote settings:',SETTINGS,'managed differences:',len(changes))
        print('Only managed keys:',json.dumps({k:current.get(k) for k in RECOMMENDED},ensure_ascii=True))
    elif action in ('apply','restore'):
        if input('Type '+action.upper()+' (Enter=cancel): ')==action.upper():
            (apply if action=='apply' else restore)()
    else:raise ValueError('ct vscode status | apply | restore')
