import SwiftUI
import MapKit
struct ProofDetailView: View {
let proof: Proof
@StateObject private var proofModeService = ProofModeService()
@State private var verificationResult: String?
@State private var showingShareSheet = false
@State private var shareItems: [Any] = []
@State private var proofFilesList: [String] = []
@State private var mapRegion: MKCoordinateRegion?
var body: some View {
ScrollView {
VStack(spacing: 20) {
// Image
if let image = proof.fullImage {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxHeight: 300)
.cornerRadius(12)
} else {
RoundedRectangle(cornerRadius: 12)
.fill(Color.gray.opacity(0.3))
.frame(height: 200)
.overlay(
Image(systemName: "photo")
.font(.system(size: 50))
.foregroundColor(.gray)
)
}
// Basic Info
VStack(alignment: .leading, spacing: 12) {
InfoRow(label: "File Name", value: proof.fileName)
InfoRow(label: "Created", value: proof.formattedDate)
InfoRow(label: "Hash", value: String(proof.hash.prefix(20)) + "...")
InfoRow(label: "File Size", value: formatFileSize(proof.fileSize))
}
.padding()
.background(Color.gray.opacity(0.05))
.cornerRadius(12)
// Verification Section
VStack(spacing: 16) {
Button(action: {
verifyProof()
}) {
HStack {
if proofModeService.isVerifying {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.scaleEffect(0.8)
} else {
Image(systemName: "checkmark.shield")
}
Text(proofModeService.isVerifying ? "Verifying..." : "Verify Proof")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(12)
}
.disabled(proofModeService.isVerifying)
// Progress indicator
if proofModeService.isVerifying {
VStack(spacing: 8) {
ProgressView(value: proofModeService.progress)
.progressViewStyle(LinearProgressViewStyle())
if !proofModeService.status.isEmpty {
Text(proofModeService.status)
.font(.caption)
.foregroundColor(.secondary)
}
}
.padding(.horizontal)
}
// Verification Result
if let result = verificationResult {
VerificationResultView(result: result)
}
}
.padding()
// Share Proof Files
VStack(spacing: 12) {
Button(action: {
shareProofFiles()
}) {
HStack {
Image(systemName: "square.and.arrow.up")
Text("Share Proof Files")
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.orange)
.foregroundColor(.white)
.cornerRadius(12)
}
if !proofFilesList.isEmpty {
VStack(alignment: .leading, spacing: 4) {
Text("Files on disk:")
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.secondary)
ForEach(proofFilesList, id: \.self) { name in
Text(name)
.font(.system(.caption2, design: .monospaced))
.foregroundColor(.primary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color.gray.opacity(0.05))
.cornerRadius(8)
}
}
.padding()
}
.padding()
}
.navigationBarTitleDisplayMode(.inline)
.sheet(isPresented: $showingShareSheet) {
ShareSheet(items: shareItems)
}
.onAppear {
setupMapRegion()
loadProofFilesList()
}
}
private func setupMapRegion() {
if let location = proof.location {
mapRegion = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: location.latitude,
longitude: location.longitude
),
span: MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01)
)
}
}
private func verifyProof() {
print("DEBUG: Starting verification for \(proof.fileName)")
verificationResult = nil // Reset previous result
proofModeService.verifyProof(proof) { result in
switch result {
case .success(let verificationOutput):
print("DEBUG: Verification successful, output length: \(verificationOutput.count)")
print("DEBUG: Verification output: \(verificationOutput)")
verificationResult = verificationOutput.isEmpty ? "Verification completed but no output received" : verificationOutput
case .failure(let error):
print("DEBUG: Verification failed with error: \(error)")
verificationResult = "Verification failed: \(error.localizedDescription)"
}
}
}
private func loadProofFilesList() {
guard let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
proofFilesList = ["ERROR: No documents directory"]
return
}
let proofmodeDir = documentsPath.appendingPathComponent("proofmode")
let hashDir = proofmodeDir.appendingPathComponent(proof.hash)
var info: [String] = []
info.append("Hash: \(proof.hash)")
info.append("Path: \(hashDir.path)")
info.append("Dir exists: \(FileManager.default.fileExists(atPath: hashDir.path))")
// Also check if proofmode dir exists and list its contents
if FileManager.default.fileExists(atPath: proofmodeDir.path) {
if let subdirs = try? FileManager.default.contentsOfDirectory(atPath: proofmodeDir.path) {
info.append("Proof dirs: \(subdirs.count)")
for sub in subdirs.prefix(5) {
info.append(" \(sub)")
}
}
} else {
info.append("proofmode/ dir does not exist")
}
do {
let files = try FileManager.default.contentsOfDirectory(at: hashDir, includingPropertiesForKeys: [.fileSizeKey])
for url in files.sorted(by: { $0.lastPathComponent < $1.lastPathComponent }) {
let size = (try? url.resourceValues(forKeys: [.fileSizeKey]).fileSize) ?? 0
info.append("\(url.lastPathComponent) (\(size) bytes)")
}
} catch {
info.append("No files: \(error.localizedDescription)")
}
proofFilesList = info
}
private func shareProofFiles() {
var items: [Any] = []
// Always include the original image if available
if let imageData = proof.imageData {
items.append(imageData as Any)
}
// Add proof sidecar files if they exist
if let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let hashDir = documentsPath.appendingPathComponent("proofmode").appendingPathComponent(proof.hash)
if let files = try? FileManager.default.contentsOfDirectory(at: hashDir, includingPropertiesForKeys: nil) {
for file in files {
items.append(file as Any)
}
}
}
shareItems = items
showingShareSheet = true
}
private func exportProof() {
// Export proof bundle
let exportData = proof.exportData()
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let exportPath = documentsPath.appendingPathComponent("\(proof.fileName).proof")
do {
try exportData.write(to: exportPath)
print("Proof exported to: \(exportPath)")
} catch {
print("Export failed: \(error)")
}
}
private func formatFileSize(_ bytes: Int64) -> String {
let formatter = ByteCountFormatter()
formatter.countStyle = .file
return formatter.string(fromByteCount: bytes)
}
}
struct InfoRow: View {
let label: String
let value: String
var body: some View {
HStack {
Text(label)
.foregroundColor(.secondary)
Spacer()
Text(value)
.fontWeight(.medium)
.multilineTextAlignment(.trailing)
}
.font(.subheadline)
}
}
struct VerificationResultView: View {
let result: String
var body: some View {
VStack(alignment: .leading, spacing: 12) {
ScrollView {
Text(result)
.font(.system(.caption, design: .monospaced))
.textSelection(.enabled)
.frame(maxWidth: .infinity, alignment: .leading)
}
.frame(maxHeight: 300)
.padding()
.background(Color.gray.opacity(0.05))
.cornerRadius(8)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(Color.gray.opacity(0.3), lineWidth: 1)
)
HStack {
Button("Copy Results") {
UIPasteboard.general.string = result
}
.font(.caption)
.foregroundColor(.blue)
Spacer()
Text("Tap to select text")
.font(.caption2)
.foregroundColor(.secondary)
}
}
.padding()
.background(Color.blue.opacity(0.05))
.cornerRadius(12)
}
}
struct ShareSheet: UIViewControllerRepresentable {
let items: [Any]
func makeUIViewController(context: Context) -> UIActivityViewController {
UIActivityViewController(activityItems: items, applicationActivities: nil)
}
func updateUIViewController(_ uiViewController: UIActivityViewController, context: Context) {}
}