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

import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.json.JSONException;
import org.json.JSONObject;

/** Compact receipts only: never accepts request bodies or GUI dumps. */
public final class JevEvents implements Closeable {
    private static final Set<String> EVENTS = new HashSet<String>(Arrays.asList(
        "PROBE_NAVIGATION_ELIGIBILITY","PROBE_NAVIGATION_CANDIDATE","PROBE_NAVIGATION_SELECTED","PROBE_NAVIGATION_ARRIVED","PROBE_NAVIGATION_ENDED",
        "STATE_SCREENSHOT_FAILED", "PROBE_SCREENSHOT_FAILED", "HISTORY_INPUT_SUMMARY", "HISTORY_PAIRWISE_COST",
        "SEMANTIC_DECISION_STORED","SEMANTIC_CONTEXT_INVALIDATED","SEMANTIC_LEDGER_SNAPSHOT","SEMANTIC_LEDGER_SNAPSHOT_END","SEMANTIC_SELECTION_REJECTED",
        "REGION_LOCATOR_REBOUND","REGION_LOCATOR_SNAPSHOT_INVALID","REGION_LOCATOR_REQUEST","REGION_LOCATOR_COMPLETION","REGION_LOCATOR_CONFIG","REGION_LOCATOR_WARMUP","REGION_LOCATOR_ANALYSIS","REGION_LOCATOR_ERROR","REGION_LOCATOR_ENTRY_TOTAL","REGION_LOCATOR_TOTAL","REGION_LOCATOR_OPPORTUNITY","REGION_LOCATOR_VISIT",
        "REGION_MEMBERSHIP_VERSION","REGION_MEMBERSHIP_NOMINATION",
        "REGION_BUDGET_FALLBACK","REGION_RULE_SOURCE_TOTAL","REGION_REVIEW_ELIGIBLE","REGION_AUTO_DUPLICATE_AVOIDED","REGION_RULE_TOTAL","REGION_INTERVENTION_TOTAL",
        "REGION_KEEP_CACHE_HIT","REGION_KEEP_REREVIEW","REGION_KEEP_REVIEWED","REGION_KEEP_SHADOW_MATCH","REGION_KEEP_SHADOW_SELECTION",
        "REGION_KEEP_SHADOW_EXECUTION","REGION_KEEP_SHADOW_TOTAL","REGION_KEEP_TOTAL","REGION_CANDIDATE_NOMINATED",
        "REGION_SHADOW_CANDIDATE_NOMINATED","REGION_SHADOW_EXACT_AVAILABLE","REGION_SHADOW_MODEL_ACTION_SELECTED","REGION_SHADOW_WOULD_GATE","REGION_SHADOW_EXECUTION","REGION_SHADOW_CANDIDATE_TOTAL","REGION_SHADOW_TOTAL",
        "PROBE_CONFIG","PROBE_SUGGESTED","PROBE_SYNC_REQUEST","PROBE_SYNC_RESULT","PROBE_REQUEST_ERROR","PROBE_NO_CANDIDATES",
        "PROBE_PACKET_SENT","PROBE_PACKET_RESULT",
        "REGION_SKIP_RESTORED","REGION_SKIP_UNOBSERVED","REGION_FALLBACK_INPUT","REGION_FALLBACK_UNOBSERVED","REGION_NOMINATED","REGION_REVIEW_REQUEST","REGION_REVIEW_RESULT","REGION_REVIEW_ERROR","REGION_GATE_ATTEMPT","REGION_ACTUAL_SKIP","REGION_ACTUAL_EXECUTION","REGION_INPUT_FAILED","REGION_INPUT_STARTED","REGION_INPUT_INTERRUPTED",
        "PROBE_SIBLINGS_PAUSED","PROBE_SIBLINGS_RESUMED",
        "REGION_WARMUP_STARTED","REGION_MEMBERS","REGION_ENTRY_ADDED","REGION_CLOSED","REGION_INSTALLED","REGION_STALE","REGION_FAILED","REGION_FILTERED","REGION_CONFIG","REGION_CONTEXT_CAPTURED","REGION_PATH_CANCELLED",
        "PROJECTION_TIMING","PROBE_LIFECYCLE","PROBE_ACCOUNTING_FINAL","REPRESENTATION_INVALIDATED",
        "REQUEST_BUDGET_EXCEEDED","AUDIT_DELIVERY_FAILED","JEV_REQUEST_ATTEMPT","DECISION_UNHEALTHY","RECEIPT_FAILED",
        "PROBE_AVAILABILITY","PROBE_BOUND","PROBE_PENDING_AT_END","NAVIGATION_STARTED","NAVIGATION_ENDED","NAVIGATION_PROJECTION_SUPPRESSED",
        "SATURATION_TRIGGER", "NEW_REALIZATION_TRIGGER", "CANDIDATE_OBSERVED", "CANDIDATE_REJECTED",
        "AUDIT_DEDUPLICATED", "JEV_REQUEST_SENT",
        "JEV_RESULT_RECEIVED", "JEV_REQUEST_FAILED", "JEV_RESULT_STALE",
        "DECISION_REPRESENTED", "DECISION_PROBE", "DECISION_REVIEWED",
        "SEMANTIC_WAIT_STARTED", "SEMANTIC_RESPONSE_CONSUMED", "SEMANTIC_FAIL_OPEN", "SEMANTIC_REVIEW_STARTED", "SEMANTIC_REVIEW_TIMING",
        "PROBE_PENDING", "PROBE_CLAIMED",
        "PROBE_SELECTED", "PROBE_EXECUTED", "PROBE_EXECUTION_FAILED",
        "NATIVE_TRANSITION", "RUN_FINISHED", "AUDIT_UNSENT", "AUDIT_RETIRED", "AUDIT_ACCEPTED", "REQUEST_SPLIT", "JEV_COUNTERS", "JEV_HISTORY_DEFERRED",
        "PROBE_INPUT_GENERATED","PROBE_INPUT_COMPLETED","PROBE_INPUT_ERROR","PROBE_INTERRUPTED",
        "SELECTION_DECISION","STATE_FINAL","MODEL_ACTION_FINAL"));
    private static final Set<String> FIELDS = new HashSet<String>(Arrays.asList(
        "confidence_pass","source_valid","same_state_confirmed","related_execution_suppressed","navigation_arrived",
        "screenshot_side","binding_reason","source_match_count","target_match_count","audit_path",
        "semantic_observability_schema","decision_role","concrete_identity","decision_scope","replaces_entry_id","decision_count","selection_context_only","input_signature",
        "review_id","review_entry_ns","prepare_ns","local_evidence_ns","provider_wait_ns","other_prepare_ns","post_prepare_selection_ns",
        "history_enumeration_ns","evidence_build_ns","prepare_local_ns","confirmed_input_objects","unknown_mapping_count",
        "history_complete","recoverable_history_count","pair_count","evidence_bytes","evidence_view_version","source_input_signature",
        "aggregation_policy","total_history","selected_history","comparison_count","same_count","different_count","insufficient_count",
        "trusted_same_count","trusted_different_count","low_confidence_count","full_history_compared","exploratory",
        "new_confirmation_object","confirmation_set_objects","confirmed_summary_events","unreported","source_tree","source_gui_timestamp",
        "locator_schema","attempt_elapsed_ns","novelty_definition","locator_period_ms","schedule_schema","scheduled_elapsed_ns","submit_elapsed_ns","completion_elapsed_ns","skipped_before_submit",
        "snapshot_abstraction_version","snapshot_topology_version","snapshot_concrete_version","snapshot_fact_version","status","stale_reasons",
        "observed_elapsed_ns","skipped_while_busy","next_scheduled_elapsed_ns","current_abstraction_version","current_topology_version","current_concrete_version","current_fact_version",
        "region_warmup_minutes","recent_window_seconds","min_recent_visits","avg_visits_threshold","shadow_only","locator_receipt","run_elapsed_ms",
        "analysis_accepted","reviewable_entries","identity_rejected_entries","mixed_target_entries","structural_regions","mature_regions","nominated_regions","nominated_entries","legacy_candidates","current_graph_matches","model_approved_entries",
        "locator_entry_id","first_nomination_elapsed_ms","legacy_first_elapsed_ms","lead_ms","analyses","run_start_monotonic_ns","elapsed_ms","opportunity_kind","observation_ns",
        "region_auto","auto_skip_probability","first_eligible","eligible_candidate_occurrences","unique_eligible_candidates",
        "rule_source","max_request_bytes","fallback_receipt","auto_rules_installed","rules_installed","duplicate_installs_avoided","rules_with_gate","gate_confirmed_executions",
        "first_install_step","first_gate_step","first_skip_step","nomination_step","nomination_region_state_count","nomination_execution_count","nomination_resolved_node_count",
        "keep_shadow_enabled","keep_id","review_status","first_keep_step","last_review_execution_count","candidate_id",
        "region_count","region_state_count","union_state_count","region_edge_count","region_edge_definition","region_native_state_visits","region_native_state_revisits",
        "known_target_count","utg_state_count","native_actions_so_far","remaining_native_actions","remaining_native_actions_known","remaining_time_ms",
        "shadow_observation_id","source_match","model_action_match","exact_match","concrete_available","concrete_enabled","action_enabled_valid",
        "shadow_source_misses","shadow_source_matches","shadow_action_matches","shadow_exact_matches","shadow_selected","shadow_executed","model_invocations","keep_cache_hits","keep_rereviews","keep_candidates","suppress_rules",
        "probe_enabled","confidence_threshold","timeout_ms","failure_cooldown_ms","probe_receipt_schema","episode_id","novelty_known","input_confirmed","selection_rank","rerequest","probe_pending",
        "attempt_id","selection_step","fallback_action_type","fallback_model_action_id","fallback_full_name",
        "wait_mode","fallback","semantic_selection_blocked","input_fingerprint","retry_count","semantic_review_enabled","semantic_review_synchronous",
        "region_seed","region_shadow_audit","shadow_audit","shadow_candidate_id","first_nomination","first_nomination_step","first_nomination_time_ms","repeat_nomination_count","future_exact_availability_count","future_model_action_selection_count","future_would_gate_count","future_confirmed_execution_count","first_would_gate_distance","would_gate_sequence","steps_since_nomination","future_execution_zero","would_gate_zero","unique_candidates","nomination_occurrences","singleton_first_candidates","multi_state_first_candidates","gate_attempts","actual_skips",
        "keep_probability","skip_probability","gui_identity","sample","skipped",
        "self_loop","activity_unchanged","gui_unchanged","sibling_count",
        "expected_abstraction_version","expected_topology_version","expected_concrete_version","expected_fact_version","current_state_changed","matching_context_changed",
        "source_activity","warmup_ms","region_receipt_schema","full_name","node_text","resource_id","target_states","confirmed_executions","region_members","saturated_settled_count","filter_channel","first_hit",
        "generation","topology_version","concrete_version","fact_version","blocked","isolated_states","concrete_id","region_enabled","update_interval_ms",
        "updated_states","updated_actions","queue_depth","queue_peak","evidence_healthy","final_state_count","terminal","admitted","natural_execution","rebuild_match","source_tree_step","occluded_node","foreground_node","occluded_bounds","foreground_bounds",
        "occluded_branch_order","foreground_branch_order",
        "review_source","represented_source","selection_version","completion_version",
        "selected_effective_unvisited","selected_navigation_target","effective_model_action_unvisited","navigation_target",
        "effective_unvisited_model_action_count","effective_state_saturated","local_probe_action_count","navigation_action_count",
        "candidate_review_rules_bytes","history_contexts_bytes","outcome_count","body_bytes","limit_bytes","state_bytes","questions_bytes","source_context_bytes","candidates_bytes","candidates_count",
        "current_siblings_bytes","current_siblings_count","executed_history_other_pages_bytes","executed_history_other_pages_count",
        "attempt","phase","http_status","exception","message","decision_healthy","local","navigation","source","changed_node","before_bounds","after_bounds",
        "wait_ns","first_local_step","local_steps","navigation_steps","last_availability","path_id","source_state","path_length",
        "native_unsaturated_actions","probe_only_actions","probe_entries","navigation_threshold","navigation_priority","navigation_high_confidence_threshold","elapsed_steps","local_probe_actions",
        "entry_id", "request_id", "transition_id", "candidate_count", "history_count",
        "physical_request_id","physical_packet_count",
        "request_bytes", "evidence_digest", "latency_ns",
        "decision_step", "decision_monotonic_ns", "reason", "error", "sampling_policy",
        "p_probe", "p_represented", "raw_choice", "raw_answer", "parsed_outcome", "ledger_status", "representative_model_action_id", "decision_request_id",
        "new_state", "new_model_action", "refinement", "action_type", "run_id",
        "jev_enabled", "elapsed_ns", "native_model_actions", "confidence", "choice", "retired", "wall_time_ms", "arrived", "probe_only", "failures", "target_state",
        "dedup_count", "unsent_deferrals",
        "event_count","input_phase",
        "injected_success","injected_failed","discarded","unreported","inject_code","event_type",
        "state_visits","state_first_seen","state_last_seen","state_saturated","state_saturation",
        "native_state_saturated","native_unsaturated_model_action_count",
        "model_action_count","enabled_valid_action_count","unsaturated_model_action_count",
        "unvisited_model_action_count","enabled_valid_unvisited_action_count","unvisited_model_action_ids",
        "snapshot_reason","model_action_type","model_action_visits","model_action_unvisited",
        "model_action_saturated","native_model_action_saturated","resolved_node_count","action_fingerprint",
        "selection_strategy","selection_context","selection_buffer_size_after",
        "selected_model_action_type","selected_model_action_saturated","selected_model_action_unvisited","selected_local_priority",
        "selected_model_action_visits","selected_action_fingerprint","selected_resolved_node_count",
        "region_context_audit","context_snapshot_id","execution_context_id","gate_context_id",
        "region_membership_audit","membership_receipt","region_membership_receipt","region_component_ids",
        "nomination_membership_receipt","nomination_component_ids","source_gui_id","target_gui_id"));
    private final java.util.concurrent.locks.ReentrantLock budgetLock=new java.util.concurrent.locks.ReentrantLock();
    private Writer writer;
    private volatile boolean healthy = true;
    private boolean closed;
    private int unflushed; // writer thread only
    private final JevOutput output;
    private final boolean ownsOutput;

    public JevEvents(File file) {this(file,new JevOutput(new JevTiming()),true);}
    JevEvents(File file,JevOutput output) {this(file,output,false);}
    private JevEvents(File file,JevOutput output,boolean owns) {
        this.output=output;ownsOutput=owns;
        try{writer=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,true),"UTF-8"));}
        catch(IOException e){fail(e);}
    }
    public JevEvents(Writer writer) {this.writer=writer;output=writer==null?null:new JevOutput(new JevTiming());ownsOutput=true;}
    public static JevEvents disabled(){return new JevEvents((Writer)null);}
    public boolean isHealthy(){return healthy && (output==null || output.isHealthy());}
    public void flush(){if(output!=null && writer!=null && !closed){
        final Writer target=writer;
        output.submit("event_io","event_flush",new JevOutput.Write(){public void run()throws IOException{target.flush();unflushed=0;}});
        output.drain();
    }}

    public void emit(String event,long step,long version,String state,String action,JSONObject fields) {JevDeadline.lock(budgetLock);try {
        if(writer==null || closed)return;
        try {
            if(!EVENTS.contains(event))throw new JSONException("Unknown compact event: "+event);
            final java.util.Map<String,Object> record=new java.util.LinkedHashMap<String,Object>();
            record.put("event",event);record.put("step",step);record.put("monotonic_ns",System.nanoTime());
            record.put("wall_time_ms",System.currentTimeMillis());record.put("abstraction_version",version);
            record.put("state_id",state==null?JSONObject.NULL:state);record.put("model_action_id",action==null?JSONObject.NULL:action);
            JSONObject identity=com.android.commands.monkey.ape.utils.ApeSnapshot.identity();
            Iterator<?> identityKeys=identity.keys();while(identityKeys.hasNext()){String key=(String)identityKeys.next();record.put(key,identity.get(key));}
            if(fields!=null) {
                Iterator<?> keys=fields.keys();while(keys.hasNext()) {
                    String key=(String)keys.next();Object value=fields.get(key);
                    if(!FIELDS.contains(key) || !(value==JSONObject.NULL || value instanceof String || value instanceof Boolean || value instanceof Number))
                        throw new JSONException("Not a compact receipt field: "+key);
                    if(value instanceof Number && (Double.isNaN(((Number)value).doubleValue()) || Double.isInfinite(((Number)value).doubleValue())))
                        throw new JSONException("Nonfinite receipt field: "+key);
                    record.put(key,value);
                }
            }
            final Writer target=writer;
            output.submit("event_io","event_"+event,new JevOutput.Write(){public void run()throws Exception{
                target.write(new JSONObject(record).toString());target.write('\n');
                // Flush idle output promptly, but amortize flushes in nomination bursts.
                if(++unflushed>=64 || !output.pendingWrites()){target.flush();unflushed=0;}
            }});
        } catch(JSONException e){fail(e);}
    } finally {budgetLock.unlock();}}
    private void fail(Exception failure){healthy=false;if(output!=null)output.fail(failure);}
    public void close() {
        final Writer target;
        JevDeadline.lock(budgetLock);try{if(closed)return;closed=true;target=writer;writer=null;} finally {budgetLock.unlock();}
        if(target!=null)output.submit("event_io",new JevOutput.Write(){public void run()throws IOException{target.close();}});
        if(ownsOutput && output!=null)output.close();
    }
}
