import React, { useEffect, useState, useRef } from "react"; import { View, Text, StyleSheet, Image, Animated, Dimensions, Alert, ScrollView, ActivityIndicator, } from "react-native"; import { useRoute, useNavigation, RouteProp } from "@react-navigation/native"; import { LinearGradient } from "expo-linear-gradient"; import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"; import { Button } from "../../components/ui/Button"; import { Card } from "../../components/ui/Card"; import { issueService } from "../../services/issueService"; import { cacheService } from "../../services/cacheService"; import { useAuth } from "../../context/AuthContext"; import { colors, spacing, typography, borderRadius } from "../../theme"; import { LocationData, FlowStep } from "../../types"; const { width } = Dimensions.get("window"); type ProcessingRouteParams = { Processing: { imageUri: string; location: LocationData; description?: string; }; }; interface AgentStep { name: string; iconName: keyof typeof Ionicons.glyphMap; label: string; status: "pending" | "running" | "done" | "error"; decision?: string; reasoning?: string; } const initialAgents: AgentStep[] = [ { name: "VisionAgent", iconName: "eye", label: "Vision Agent", status: "pending", }, { name: "GeoDeduplicateAgent", iconName: "map", label: "Geo Agent", status: "pending", }, { name: "PriorityAgent", iconName: "alert-circle", label: "Priority Agent", status: "pending", }, { name: "RoutingAgent", iconName: "git-branch", label: "Routing Agent", status: "pending", }, { name: "NotificationAgent", iconName: "notifications", label: "Notification Agent", status: "pending", }, ]; export function ProcessingScreen() { const route = useRoute>(); const navigation = useNavigation(); const { session } = useAuth(); const { imageUri, location, description } = route.params; const [agents, setAgents] = useState(initialAgents); const [issueId, setIssueId] = useState(null); const [error, setError] = useState(null); const [isComplete, setIsComplete] = useState(false); const progressAnim = useRef(new Animated.Value(0)).current; const scanLineAnim = useRef(new Animated.Value(0)).current; const pulseAnims = useRef( initialAgents.map(() => new Animated.Value(1)), ).current; useEffect(() => { submitIssue(); Animated.loop( Animated.sequence([ Animated.timing(scanLineAnim, { toValue: 1, duration: 2000, useNativeDriver: true, }), Animated.timing(scanLineAnim, { toValue: 0, duration: 0, useNativeDriver: true, }), ]), ).start(); }, []); useEffect(() => { let cleanupFn: (() => void) | undefined; const connect = async () => { if (!issueId) return; try { cleanupFn = await issueService.connectToFlowStream( issueId, (event) => { handleStreamEvent(event); }, (err) => {}, ); } catch (e) { console.error("Failed to connect to stream", e); } }; connect(); return () => { if (cleanupFn) cleanupFn(); }; }, [issueId]); const handleStreamEvent = (event: any) => { if (event.type === "step_started") { const { agent_name } = event.data; setAgents((prev) => prev.map((a) => a.name === agent_name ? { ...a, status: "running" } : a, ), ); const idx = agents.findIndex((a) => a.name === agent_name); if (idx !== -1) { pulseAnims[idx].setValue(1); Animated.loop( Animated.sequence([ Animated.timing(pulseAnims[idx], { toValue: 1.1, duration: 500, useNativeDriver: true, }), Animated.timing(pulseAnims[idx], { toValue: 1, duration: 500, useNativeDriver: true, }), ]), ).start(); } } else if (event.type === "step_completed") { const { agent_name, decision, reasoning, result } = event.data; setAgents((prev) => prev.map((a) => a.name === agent_name ? { ...a, status: "done", decision, reasoning } : a, ), ); const idx = agents.findIndex((a) => a.name === agent_name); if (idx !== -1) { pulseAnims[idx].stopAnimation(); pulseAnims[idx].setValue(1); } if (agent_name === "VisionAgent" && result?.needs_confirmation) { showConfirmationDialog(); } } else if (event.type === "flow_completed") { setIsComplete(true); Animated.timing(progressAnim, { toValue: 1, duration: 300, useNativeDriver: false, }).start(); if (event.data.final_result?.needs_confirmation) { setIsComplete(false); showConfirmationDialog(); } } else if (event.type === "flow_error") { setError(event.data.error || "Unknown error"); } }; useEffect(() => { const doneCount = agents.filter((a) => a.status === "done").length; const runningCount = agents.filter((a) => a.status === "running").length; const total = agents.length; const progress = (doneCount + (runningCount ? 0.5 : 0)) / total; Animated.timing(progressAnim, { toValue: progress, duration: 500, useNativeDriver: false, }).start(); }, [agents]); const showConfirmationDialog = () => { Alert.alert( "Zero Detections Found", "Our AI didn't find any specific issues in this image.\n\nDo you want to submit this for manual review?\n\n⚠️ WARNING: False reports may result in account ban.", [ { text: "Cancel", style: "cancel", onPress: async () => { try { if (issueId) await issueService.confirmIssue(issueId, false); navigation.goBack(); } catch (e) { console.error(e); } }, }, { text: "Submit for Manual Review", style: "default", onPress: async () => { try { if (issueId) { await issueService.confirmIssue(issueId, true); setIsComplete(true); } } catch (e) { setError("Failed to confirm issue"); } }, }, ], ); }; const submitIssue = async () => { try { console.log( "[ProcessingScreen] Submitting issue - session present:", !!session, "access_token present:", !!session?.access_token, ); const result = await issueService.createIssue( imageUri, location, description, session?.access_token, ); setIssueId(result.issue_id); } catch (err: any) { console.error("Submit error:", err); setError(err.message || "Failed to submit issue"); } }; const handleViewDetails = async () => { if (issueId) { await cacheService.clearCache(); navigation.navigate("IssueDetail", { issueId }); } }; const handleGoHome = async () => { await cacheService.clearCache(); navigation.reset({ index: 0, routes: [{ name: "MainTabs" }], }); }; const progressInterpolate = progressAnim.interpolate({ inputRange: [0, 1], outputRange: ["0%", "100%"], }); const scanTranslateY = scanLineAnim.interpolate({ inputRange: [0, 1], outputRange: [0, 180], // Match image height }); return ( {isComplete ? ( ) : ( )} {isComplete ? "Report Processed!" : "AI Analysis in Progress"} {isComplete ? "Your issue has been categorized and routed." : "Our intelligent agents are analyzing your report."} {!isComplete && ( )} {Math.round( (agents.filter((a) => a.status === "done").length / agents.length) * 100, )} % Complete {agents.map((agent, index) => ( {agent.label} {agent.decision ? ( Result: {agent.decision} ) : null} {agent.status === "running" && ( )} ))} {error ? ( {error}