"""Inspect resources; only explicitly selected operator VSCode jobs can be stopped."""
import json
import os
from pathlib import Path
import select
import signal
import time

import field_profiles as profiles
from field_ui import print, input


def process(pid, proc=Path('/proc')):
    path = proc / str(int(pid))
    stat = (path/'stat').read_text().rsplit(')',1)[1].split()
    args = (path/'cmdline').read_bytes().split(b'\0')
    exe = os.readlink(path/'exe')
    uid = path.stat().st_uid
    role = 'other'
    if exe.startswith('/home/si/.vscode-server/'):
        if Path(exe).name == 'rg' and b'--files' in args:
            role = 'vscode-file-search'
        elif b'--type=extensionHost' in args:
            role = 'vscode-extensionHost'
    return {'pid':int(pid),'ppid':int(stat[1]),'uid':uid,'start_ticks':int(stat[19]),
            'ticks':int(stat[11])+int(stat[12]),'rss_bytes':int(stat[21])*os.sysconf('SC_PAGE_SIZE'),
            'role':role,'exe':exe}


def snapshot():
    rows = []
    for p in Path('/proc').iterdir():
        if not p.name.isdigit():continue
        try:rows.append(process(int(p.name)))
        except (OSError, ValueError):pass
    return rows


def status():
    before = {p['pid']:p for p in snapshot()};start = time.monotonic()
    time.sleep(1)
    rows = snapshot();elapsed = time.monotonic()-start;uptime = float(Path('/proc/uptime').read_text().split()[0])
    hz = os.sysconf('SC_CLK_TCK')
    for p in rows:
        b = before.get(p['pid'])
        p['cpu_percent_one_core_100'] = round(100*(p['ticks']-b['ticks'])/hz/elapsed,1) if b and b['start_ticks']==p['start_ticks'] else None
        p['age_seconds'] = max(0,int(uptime-p['start_ticks']/hz))
    print('CPU: one logical core=100%; RAM: process RSS; shared pages may overlap.')
    print('PID  UID  COMMAND  CPU%  RAM(MiB)  age(s)  role')
    hot = sorted(rows,key=lambda p:(p.get('cpu_percent_one_core_100') or 0),reverse=True)[:8]
    memory = sorted(rows,key=lambda p:p['rss_bytes'],reverse=True)[:8]
    selected = {p['pid']:p for p in hot+memory}
    for p in sorted(selected.values(),key=lambda p:-p['rss_bytes']):
        print(p['pid'],p['uid'],Path(p['exe']).name,p['cpu_percent_one_core_100'],round(p['rss_bytes']/1024**2),p['age_seconds'],p['role'])
    print('Explicit single-process stop: ct resources stop PID (no broad cleanup).')


def stop(expected, confirmed, *, proc=Path('/proc')):
    if not confirmed:raise ValueError('Explicit confirmation required')
    if expected['uid'] != profiles.account().pw_uid or expected['role'] not in ('vscode-file-search','vscode-extensionHost'):
        raise ValueError('Protected process: only the operator VSCode search/extensionHost is supported')
    fd = os.pidfd_open(expected['pid'])
    try:
        current = process(expected['pid'],proc)
        for key in ('pid','ppid','uid','start_ticks','role','exe'):
            if current[key] != expected[key]:raise ValueError('PID identity changed; no signal sent')
        signal.pidfd_send_signal(fd,signal.SIGTERM)
        poll = select.poll();poll.register(fd,select.POLLIN)
        exited = bool(poll.poll(5000))
    finally:os.close(fd)
    record = {'pid':expected['pid'],'start_ticks':expected['start_ticks'],'role':expected['role'],
              'signal':'SIGTERM','exited_within_5s':exited,'automatic_escalation':False}
    path = profiles.STATE / 'resource-actions' / (str(time.time_ns())+'.json')
    profiles.atomic(path,record)
    print(json.dumps(record),'record:',path)
    if not exited:raise RuntimeError('Process remains; no automatic SIGKILL. Inspect before another action.')


def main(args):
    if not args or args==['status']:return status()
    if len(args)!=2 or args[0]!='stop' or not args[1].isdecimal():
        raise ValueError('ct resources status | stop PID')
    p=process(int(args[1]))
    print('Selected:',{k:p[k] for k in ('pid','ppid','uid','role','rss_bytes','start_ticks')})
    if p['role']=='vscode-extensionHost':
        print('Save editor files and stop extension tasks first. Extensions reload; shell/SSH are not targeted.')
    phrase='STOP '+str(p['pid'])
    if input('Type '+phrase+' (Enter=cancel): ')!=phrase:return
    stop(p,True)
