#!/usr/bin/env python3
import sys, subprocess, os, pathlib, datetime, json, hashlib, socket
SERVICE='forenclarity.service'
STATE='/var/lib/forenclarity/service_state.json'; MANIFEST='/var/lib/forenclarity/install_manifest.json'
LOG='/var/log/forenclarity/forenclarity-service.log'
ACTIVE='/var/log/forenclarity/forenclarity-active-alerts.jsonl'; HISTORY='/var/log/forenclarity/forenclarity-historical-alerts.jsonl'; LEGACY='/var/log/forenclarity/forenclarity-alerts.jsonl'
RETENTION='/var/lib/forenclarity/alert_retention.json'; LICENSE='/var/lib/forenclarity/license_state.json'
OUT=os.path.expanduser('~/forenclarity_output/forenclarity_linux_ubuntu_v1_0_4')
BUILD='ForenClarity Linux Ubuntu v1.0.4'
def local_now(): return datetime.datetime.now().astimezone().isoformat()
def utc_now(): return datetime.datetime.now(datetime.timezone.utc).isoformat()
def run(cmd):
    p=subprocess.run(cmd,text=True,capture_output=True); return p.returncode,(p.stdout or '')+(p.stderr or '')
def read(p):
    try: return pathlib.Path(p).read_text()
    except Exception: return ''
def safe_append(path,line):
    try:
        os.makedirs(os.path.dirname(path), exist_ok=True); open(path,'a',encoding='utf-8').write(line); pathlib.Path(path).chmod(0o666); return True,''
    except PermissionError as e:
        code="import sys,pathlib; pathlib.Path(sys.argv[1]).parent.mkdir(parents=True,exist_ok=True); open(sys.argv[1],'a',encoding='utf-8').write(sys.stdin.read()); pathlib.Path(sys.argv[1]).chmod(0o666)"
        cmd=['/usr/bin/python3','-c',code,path]
        if os.geteuid()!=0: cmd=(['pkexec'] if subprocess.run(['which','pkexec'],capture_output=True).returncode==0 else ['sudo'])+cmd
        p=subprocess.run(cmd,input=line,text=True,capture_output=True); return (p.returncode==0,(p.stdout or '')+(p.stderr or '') or str(e))
    except Exception as e: return False,str(e)
def status():
    print('ForenClarity Linux Ubuntu v1.0.4 status'); print(run(['systemctl','is-active',SERVICE])[1].strip()); print(run(['systemctl','is-enabled',SERVICE])[1].strip()); print(read(STATE) or 'No state JSON yet')
def report():
    os.makedirs(OUT,exist_ok=True); stamp=datetime.datetime.now().strftime('%Y%m%d_%H%M%S'); path=os.path.join(OUT,f'forenclarity_ubuntu_v1_0_4_cli_report_{stamp}.txt')
    body='ForenClarity Linux Ubuntu v1.0.4 CLI Report\nGenerated local time: '+local_now()+'\n\nSYSTEMD:\n'+run(['systemctl','status',SERVICE,'--no-pager'])[1]+'\nSTATE:\n'+read(STATE)+'\nACTIVE ALERTS:\n'+read(ACTIVE)+'\nHISTORICAL ALERTS:\n'+read(HISTORY)+'\nRETENTION:\n'+read(RETENTION)+'\nLICENSE:\n'+read(LICENSE)+'\nMANIFEST:\n'+read(MANIFEST)+'\nLOG:\n'+read(LOG)[-12000:]
    pathlib.Path(path).write_text(body); print(path)
def svc(action):
    cmd=['systemctl',action,SERVICE]
    if os.geteuid()!=0: cmd=(['pkexec'] if subprocess.run(['which','pkexec'],capture_output=True).returncode==0 else ['sudo'])+cmd
    print(run(cmd)[1])
def alert_payload(kind='TEST_ALERT'):
    ev=f'ForenClarity v1.0.4 test alert generated by user {os.environ.get("USER","unknown")} on {socket.gethostname()}'
    aid=hashlib.sha256(ev.encode()).hexdigest()[:16]
    return {'time_utc':utc_now(),'time_local':local_now(),'product':'ForenClarity','phase':'15','build':BUILD,'alert_id':aid,'alert_type':kind,'title':'ForenClarity Linux Ubuntu v1.0.4 alert pipeline test','severity':'low','status':'active','source':'forenclarity-cli alert-test','scan_tool':'test','origin':'local host '+socket.gethostname(),'target':'none/test','user_context':os.environ.get('USER','unknown'),'command':'forenclarity-cli alert-test','evidence':ev,'explanation':'This confirms active alerts, historical alerts, CLI, GUI, cooldown-aware reports, local-time display, and alert lifecycle pipeline.','recommended_action':'No security action required. Use this to verify the alert pipeline.','dedup_key':aid,'cooldown_seconds':600}
def alert_test():
    p=alert_payload(); line=json.dumps(p,ensure_ascii=False)+'\n'; ok1,err1=safe_append(ACTIVE,line); ok2,err2=safe_append(HISTORY,line); ok3,err3=safe_append(LEGACY,line)
    if ok1 and ok2 and ok3: print('test alert written: active + historical + legacy alert files')
    else: print('ERROR writing test alert:',err1,err2,err3); sys.exit(1)
def show_file(path,label):
    txt=read(path)
    if not txt:
        print(f'No ForenClarity {label} alerts yet'); return
    # Default to recent alerts only so Ubuntu terminal output is not gigantic. Use --all for complete output.
    if '--all' in sys.argv:
        print(txt); return
    lines=[x for x in txt.splitlines() if x.strip()]
    limit=40
    for a in sys.argv[2:]:
        if a.startswith('--last='):
            try: limit=int(a.split('=',1)[1])
            except Exception: pass
    shown=lines[-limit:]
    print('\n'.join(shown))
    if len(lines)>len(shown):
        print(f'\n[ForenClarity] Showing last {len(shown)} of {len(lines)} {label} alerts. Use: forenclarity-cli {label}-alerts --all  or save with: forenclarity-cli alerts --all > ~/Desktop/forenclarity_alerts_output.txt')
def clear_active():
    msg='Active alerts cleared by forenclarity-cli clear-active'
    archive={'time_utc':utc_now(),'time_local':local_now(),'product':'ForenClarity','phase':'15','build':BUILD,'alert_id':hashlib.sha256(msg.encode()).hexdigest()[:16],'alert_type':'ACTIVE_ALERTS_CLEARED','title':'Active alerts were cleared','severity':'info','status':'historical','source':'forenclarity-cli clear-active','scan_tool':'none','origin':'local host '+socket.gethostname(),'target':'active-alert-list','user_context':os.environ.get('USER','unknown'),'command':'forenclarity-cli clear-active','evidence':msg,'explanation':'Active alert list was cleared by an authorized local user. Historical alert records are retained according to retention settings.','recommended_action':'No action required unless this clear operation was unauthorized.'}
    safe_append(HISTORY,json.dumps(archive,ensure_ascii=False)+'\n')
    code="import pathlib,sys; p=pathlib.Path(sys.argv[1]); p.parent.mkdir(parents=True,exist_ok=True); p.write_text('',encoding='utf-8'); p.chmod(0o666)"
    cmd=['/usr/bin/python3','-c',code,ACTIVE]
    if os.geteuid()!=0: cmd=(['pkexec'] if subprocess.run(['which','pkexec'],capture_output=True).returncode==0 else ['sudo'])+cmd
    rc,out=run(cmd); print('active alerts cleared' if rc==0 else out)
def set_retention(args):
    cfg={'active_cooldown_seconds':600,'history_retention_days':30,'history_max_mb':50,'active_max_alerts':500,'deduplicate_active_alerts':True,'poll_interval_seconds':1}
    try: cfg.update(json.loads(read(RETENTION) or '{}'))
    except Exception: pass
    for a in args:
        if a.startswith('--cooldown='): cfg['active_cooldown_seconds']=int(a.split('=',1)[1])
        elif a.startswith('--days='): cfg['history_retention_days']=int(a.split('=',1)[1])
        elif a.startswith('--mb='): cfg['history_max_mb']=int(a.split('=',1)[1])
        elif a.startswith('--active-max='): cfg['active_max_alerts']=int(a.split('=',1)[1])
        elif a.startswith('--poll='): cfg['poll_interval_seconds']=float(a.split('=',1)[1])
    code="import json,sys,pathlib; p=pathlib.Path(sys.argv[1]); p.parent.mkdir(parents=True,exist_ok=True); p.write_text(json.dumps(json.loads(sys.stdin.read()),indent=2),encoding='utf-8')"
    cmd=['/usr/bin/python3','-c',code,RETENTION]
    if os.geteuid()!=0: cmd=(['pkexec'] if subprocess.run(['which','pkexec'],capture_output=True).returncode==0 else ['sudo'])+cmd
    rc,out=subprocess.run(cmd,input=json.dumps(cfg),text=True,capture_output=True).returncode,''
    print(json.dumps(cfg,indent=2))

def integrity():
    manifest='/usr/share/forenclarity/INSTALLED_FILE_SHA256SUMS.txt'
    if not os.path.exists(manifest):
        print('No installed integrity manifest found'); sys.exit(2)
    bad=[]; ok=0; missing=[]
    for line in read(manifest).splitlines():
        if not line.strip() or line.startswith('#'): continue
        parts=line.split(None,1)
        if len(parts)!=2: continue
        expected,path=parts[0],parts[1].strip()
        if not os.path.exists(path):
            missing.append(path); continue
        h=hashlib.sha256(open(path,'rb').read()).hexdigest()
        if h==expected: ok+=1
        else: bad.append(path)
    print(f'ForenClarity installed-file integrity check: {ok} OK, {len(bad)} changed, {len(missing)} missing')
    if bad:
        print('Changed files:'); [print('  '+x) for x in bad]
    if missing:
        print('Missing files:'); [print('  '+x) for x in missing]
    if bad or missing: sys.exit(1)

def main():
    c=sys.argv[1] if len(sys.argv)>1 else 'status'
    if c=='status': status()
    elif c=='report': report()
    elif c in ('start','stop','restart','enable','disable'): svc(c)
    elif c=='manifest': print(read(MANIFEST) or 'No manifest found')
    elif c=='logs': print(read(LOG) or 'No log found')
    elif c=='license': print(read(LICENSE) or 'No license cache found')
    elif c in ('alerts','active-alerts'): show_file(ACTIVE,'active')
    elif c in ('history','historical-alerts'): show_file(HISTORY,'historical')
    elif c=='alert-test': alert_test()
    elif c=='clear-active': clear_active()
    elif c=='retention': print(read(RETENTION) or 'No retention config found')
    elif c=='set-retention': set_retention(sys.argv[2:])
    elif c=='verify': subprocess.run(['/bin/bash','/usr/share/forenclarity/Verify_ForenClarity_Ubuntu.sh']) if os.path.exists('/usr/share/forenclarity/Verify_ForenClarity_Ubuntu.sh') else status()
    elif c=='integrity': integrity()
    elif c=='version': print('ForenClarity Linux Ubuntu v1.0.4')
    else: print('Usage: forenclarity-cli status|report|alerts [--last=40|--all]|history [--last=40|--all]|alert-test|clear-active|retention|set-retention --days=30 --mb=50 --cooldown=600 --poll=1|start|stop|restart|enable|disable|manifest|logs|license|verify|integrity|version'); sys.exit(1)
if __name__=='__main__': main()
