#!/usr/bin/python3 -I
from __future__ import annotations
import json, os, subprocess, sys
sys.path.insert(0,"/usr/lib/fusionos/cli")
from permissions import PermissionError as FusionPermissionError, require_caller
sys.path.insert(0,"/usr/lib/python3/dist-packages")
from fusionos_store.catalog import AptCatalog, CommandResult, validate_repository_contract
from fusionos_store.transaction import MANAGED_PACKAGES_PATH, prepare, read_managed_packages, write_managed_packages, write_receipt
MAXIMUM_REQUEST_BYTES=65536

def run(command,timeout):
    try: c=subprocess.run(command,check=False,capture_output=True,text=True,timeout=timeout,env={"LC_ALL":"C","LANG":"C","DEBIAN_FRONTEND":"noninteractive","PATH":"/usr/sbin:/usr/bin:/sbin:/bin"})
    except subprocess.TimeoutExpired as e: return CommandResult(124,e.stdout if isinstance(e.stdout,str) else "",e.stderr if isinstance(e.stderr,str) else "Command timed out.")
    except OSError as e: return CommandResult(127,"",str(e))
    return CommandResult(c.returncode,c.stdout,c.stderr)
def read_request(op):
    raw=sys.stdin.buffer.read(MAXIMUM_REQUEST_BYTES+1)
    if len(raw)>MAXIMUM_REQUEST_BYTES: raise ValueError("Request exceeds allowed size.")
    try: data=json.loads(raw.decode("utf-8"))
    except Exception as e: raise ValueError("Request is not valid JSON.") from e
    expected={"schema","action","roots"} if op=="prepare" else {"schema","action","roots","plan_id","package_count"}
    if not isinstance(data,dict) or data.get("schema")!=1 or set(data)!=expected: raise ValueError("Request fields do not match schema 1.")
    if data["action"] not in {"install","update","update-all","uninstall"}: raise ValueError("Invalid Store action.")
    if not isinstance(data["roots"],list) or not all(isinstance(x,str) for x in data["roots"]): raise ValueError("Invalid application root list.")
    return data
def refresh():
    validate_repository_contract(require_root_owned_state=True); audit=run(("/usr/bin/dpkg","--audit"),60)
    if audit.returncode!=0 or audit.stdout.strip(): raise ValueError("The FusionOS package database requires administrative repair before using FusionStore.")
    metadata=run(("/usr/bin/apt-get","-o","Acquire::AllowInsecureRepositories=false","update"),300)
    if metadata.returncode!=0: raise ValueError("Authenticated FusionStore catalogue refresh failed.")
    return AptCatalog(apt_cache_command="/usr/bin/apt-cache",dpkg_query_command="/usr/bin/dpkg-query",runner=run,require_root_owned_state=True)
def response_plan(plan): return {"schema":1,"success":True,"action":plan.action,"roots":list(plan.roots),"packages":[{"package":p,"version":v} for p,v in plan.versions],"plan_id":plan.plan_id,"package_count":plan.package_count,"error":None}
def apply_plan(req):
    catalog=refresh(); current=prepare(req["action"],tuple(req["roots"]),catalog,run,MANAGED_PACKAGES_PATH)
    if current.plan_id!=req["plan_id"] or current.package_count!=req["package_count"] or tuple(req["roots"])!=current.roots: return {"schema":1,"success":False,"message":"Application transaction was not applied.","error":"The application transaction changed after confirmation. Run the command again and review the new plan."}
    managed=set(read_managed_packages(MANAGED_PACKAGES_PATH,require_safe_state=True))
    if current.action in {"install","update","update-all"}:
        if current.versions:
            exact=[f"{package}={version}" for package,version in current.versions]
            result=run(("/usr/bin/apt-get","--yes","--no-remove","--no-install-recommends","install","--",*exact),7200)
            if result.returncode!=0: return {"schema":1,"success":False,"message":"Application transaction failed.","error":"FusionStore package installation failed."}
            for package,version in current.versions:
                installed=run(("/usr/bin/dpkg-query","-W","-f=${db:Status-Abbrev}\t${Version}\n","--",package),30)
                if installed.returncode!=0 or installed.stdout.strip()!=f"ii \t{version}":
                    return {"schema":1,"success":False,"message":"Application transaction requires review.","error":f"An installed package differs from the approved FusionStore plan: {package}"}
        managed.update(current.roots)
    else:
        result=run(("/usr/bin/apt-get","--yes","remove","--",current.roots[0]),7200)
        if result.returncode!=0: return {"schema":1,"success":False,"message":"Application transaction failed.","error":"FusionStore package removal failed."}
        removed=run(("/usr/bin/dpkg-query","-W","-f=${db:Status-Abbrev}","--",current.roots[0]),30)
        if removed.returncode==0 and removed.stdout.strip().startswith("ii "):
            return {"schema":1,"success":False,"message":"Application transaction requires review.","error":"The removed FusionStore application is still installed."}
        managed.discard(current.roots[0])
    audit=run(("/usr/bin/dpkg","--audit"),60)
    if audit.returncode!=0 or audit.stdout.strip(): return {"schema":1,"success":False,"message":"Application transaction requires review.","error":"Package database audit did not complete cleanly after the transaction."}
    write_managed_packages(sorted(managed),MANAGED_PACKAGES_PATH); write_receipt(current.action,current.roots,current.plan_id); return {"schema":1,"success":True,"message":"FusionOS application transaction completed.","error":None}
def main():
    if len(sys.argv)!=2 or sys.argv[1] not in {"prepare","apply"}: print("FusionStore transaction helper: invalid operation",file=sys.stderr); return 2
    try:
        if os.geteuid()!=0: raise ValueError("root privileges are required")
        try: require_caller("fusionos.store.manage")
        except FusionPermissionError as e: raise ValueError(str(e)) from e
        req=read_request(sys.argv[1]); output=response_plan(prepare(req["action"],tuple(req["roots"]),refresh(),run,MANAGED_PACKAGES_PATH)) if sys.argv[1]=="prepare" else apply_plan(req); print(json.dumps(output,sort_keys=True)); return 0
    except ValueError as e: print(f"FusionStore transaction helper: {e}",file=sys.stderr); return 2
if __name__=="__main__": raise SystemExit(main())
