package com.android.commands.monkey.ape.jev;

import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
import org.json.*;

/** Pure saved-byte replay. No transport, Provider, runner or device calls. */
public final class GUITabooWireReplay {
    static JSONObject read(String p)throws Exception{return new JSONObject(new String(Files.readAllBytes(Paths.get(p)),"UTF-8"));}
    static String bodyLiteral(String p)throws Exception{
        String raw=new String(Files.readAllBytes(Paths.get(p)),"UTF-8");int at=raw.indexOf("\"body\":");
        if(at<0)throw new AssertionError("No raw body literal");at=raw.indexOf('{',at);int depth=0;boolean quoted=false,escaped=false;
        for(int i=at;i<raw.length();i++){char c=raw.charAt(i);
            if(quoted){if(escaped)escaped=false;else if(c=='\\')escaped=true;else if(c=='\"')quoted=false;}
            else if(c=='\"')quoted=true;else if(c=='{')depth++;else if(c=='}'&&--depth==0)return raw.substring(at,i+1);
        }throw new AssertionError("Unclosed body literal");
    }
    public static void main(String[] args)throws Exception {
        JSONArray inputs=new JSONArray(new String(Files.readAllBytes(Paths.get(args[0])),"UTF-8")),out=new JSONArray();
        Pattern binding=Pattern.compile("`candidates\\.(c[0-9]+)`.*`executed_actions\\.(h[0-9]+)`",Pattern.DOTALL);
        int errors=0,decisions=0,swappedOver=0;
        for(int i=0;i<inputs.length();i++){
            JSONObject item=inputs.getJSONObject(i),r=read(item.getString("request")),body=r.getJSONObject("body"),q=body.getJSONObject("questions");
            JSONArray pairs=new JSONArray();
            for(Iterator<?> it=q.keys();it.hasNext();){String id=(String)it.next();Matcher m=binding.matcher(q.getJSONObject(id).getString("instructions"));
                if(!m.find())throw new AssertionError("Cannot recover Pair binding: "+id);
                pairs.put(new JSONObject().put("id",id).put("candidate",m.group(1)).put("history",m.group(2)));
            }
            JSONObject plan=new JSONObject().put("state",body.getJSONObject("state")).put("pairs",pairs)
                .put("candidate_ids",r.getJSONArray("candidate_ids")).put("coverage",r.getJSONObject("history_coverage"))
                .put("history_provenance",r.getJSONObject("history_provenance")).put("input_signature",r.getString("input_signature"))
                .put("review_contract",r.getString("contract")).put("provider_endpoint",r.getString("endpoint"));
            if(r.has("evidence_view_version"))plan.put("evidence_view_version",r.getString("evidence_view_version")).put("source_input_signature",r.getString("source_input_signature"));
            JevHistoryPairwiseProtocol.Request recreated=JevHistoryPairwiseProtocol.request("offline",body.getString("model"),plan,32768);
            boolean wire=body.toString().equals(recreated.bodyJson),fingerprint=recreated.inputFingerprint.equals(r.getString("input_fingerprint")),size=recreated.requestBytes==r.getInt("request_bytes");
            String literal=bodyLiteral(item.getString("request"));
            boolean originalFingerprint=JevEvidence.digest(literal).equals(r.getString("input_fingerprint"));
            String alternate=body.getString("model").startsWith("typesafe/")?"StartLux-Decision-4B":"typesafe/jev-1.14";
            boolean sameEvidence=false,over=false;int altBytes=-1;
            try{JevHistoryPairwiseProtocol.Request alt=JevHistoryPairwiseProtocol.request("offline-alt",alternate,plan,32768);altBytes=alt.requestBytes;
                JSONObject a=new JSONObject(alt.bodyJson);sameEvidence=a.getJSONObject("state").toString().equals(body.getJSONObject("state").toString())&&a.getJSONObject("questions").toString().equals(q.toString());
            }catch(JevHistoryPairwiseProtocol.BudgetExceeded e){over=true;altBytes=e.requestBytes;swappedOver++;}
            int checked=0,mismatch=0;
            if(item.has("response")){
                JSONObject response=read(item.getString("response"));
                if(!response.isNull("raw_response")&&response.has("decisions")){
                    JevHistoryPairwiseProtocol.Result result=JevHistoryPairwiseProtocol.fromProvider(response.getString("raw_response"),recreated);
                    JSONArray saved=response.getJSONArray("decisions");
                    for(int j=0;j<saved.length();j++){JSONObject d=saved.getJSONObject(j);JevHistoryPairwiseProtocol.Decision actual=result.decisions.get(d.getString("candidate_id"));
                        checked++;if(actual==null||!actual.representativeModelActionId.equals(d.getString("aggregate_choice")))mismatch++;
                    }
                }
            }
            decisions+=checked;if(!wire||!originalFingerprint||!size||(!sameEvidence&&!over)||mismatch>0)errors++;
            out.put(new JSONObject().put("run_id",item.getString("run_id")).put("request_id",r.getString("request_id")).put("wire_matches",wire)
                .put("host_reserialization_fingerprint_matches",fingerprint).put("archive_wire_sha256_matches",originalFingerprint).put("size_matches",size).put("saved_bytes",recreated.requestBytes).put("alternate_model",alternate)
                .put("alternate_bytes",altBytes).put("alternate_over_cap",over).put("same_state_questions",sameEvidence)
                .put("saved_candidate_decisions_checked",checked).put("candidate_decision_mismatches",mismatch));
        }
        JSONObject report=new JSONObject().put("requests",inputs.length()).put("candidate_decisions_checked",decisions).put("errors",errors).put("alternate_over_cap",swappedOver).put("rows",out)
            .put("scope","Original raw body SHA256 is verified separately. Host Android JSON reserialization may reorder keys: semantic wire structure and byte length match, but its byte hash is not asserted equal. Saved closure construction and answer aggregation only; no model/transport calls; no full Native history reconstruction.");
        Files.write(Paths.get(args[1]),report.toString(2).getBytes("UTF-8"));
        System.out.println("Replayed "+inputs.length()+" saved requests; decisions="+decisions+" errors="+errors+" alternate_over_cap="+swappedOver);
        if(errors!=0)throw new AssertionError("Offline replay mismatch; see report");
    }
}
