     1→import Foundation
     2→
     3→struct Email: Identifiable, Codable {
     4→    let id: String
     5→    let threadId: String
     6→    let from: String
     7→    let to: String
     8→    let subject: String
     9→    let snippet: String
    10→    let body: String
    11→    let date: Date
    12→    var isRead: Bool
    13→    var isStarred: Bool
    14→    var labels: [String]
    15→
    16→    // Threading headers (for Reply)
    17→    var messageId: String?      // Header: Message-ID
    18→    var inReplyTo: String?      // Header: In-Reply-To
    19→    var references: String?     // Header: References
    20→
    21→    // Attachments
    22→    var attachments: [Attachment]
    23→
    24→    // AI Analysis
    25→    var analysis: EmailAnalysis?
    26→
    27→    var hasAttachments: Bool { !attachments.isEmpty }
    28→    var attachmentCount: Int { attachments.count }
    29→
    30→    var fromName: String {
    31→        if let match = from.firstMatch(of: /^([^<]+)\s*</) {
    32→            return String(match.1).trimmingCharacters(in: .whitespaces)
    33→        }
    34→        return from.components(separatedBy: "@").first ?? from
    35→    }
    36→
    37→    var fromEmail: String {
    38→        if let match = from.firstMatch(of: /<([^>]+)>/) {
    39→            return String(match.1)
    40→        }
    41→        return from
    42→    }
    43→}
    44→
    45→struct EmailAnalysis: Codable {
    46→    let summary: String
    47→    let sentiment: Sentiment
    48→    let priority: Priority
    49→    let category: Category
    50→    let actionItems: [String]
    51→    let keyTopics: [String]
    52→    let suggestedReply: String?
    53→    let analyzedAt: Date
    54→
    55→    // Phase 4: Advanced AI fields
    56→    var confidence: Double
    57→    var isSpam: Bool
    58→    var phishingRisk: PhishingRisk
    59→
    60→    enum PhishingRisk: String, Codable, CaseIterable {
    61→        case none, low, medium, high
    62→
    63→        var color: String {
    64→            switch self {
    65→            case .none: return "green"
    66→            case .low: return "yellow"
    67→            case .medium: return "orange"
    68→            case .high: return "red"
    69→            }
    70→        }
    71→
    72→        var icon: String {
    73→            switch self {
    74→            case .none: return "checkmark.shield"
    75→            case .low: return "shield"
    76→            case .medium: return "exclamationmark.shield"
    77→            case .high: return "xmark.shield.fill"
    78→            }
    79→        }
    80→    }
    81→
    82→    enum DeleteRecommendation: String, Codable, CaseIterable {
    83→        case safeToDelete   // importanceScore < 30
    84→        case review         // importanceScore 30-60
    85→        case keep           // importanceScore > 60
    86→        case suspicious     // phishingRisk >= medium OR isSpam
    87→
    88→        var icon: String {
    89→            switch self {
    90→            case .safeToDelete: return "trash.circle.fill"
    91→            case .review: return "eye.circle.fill"
    92→            case .keep: return "checkmark.circle.fill"
    93→            case .suspicious: return "exclamationmark.shield.fill"
    94→            }
    95→        }
    96→
    97→        var description: String {
    98→            switch self {
    99→            case .safeToDelete: return "Safe to delete"
   100→            case .review: return "Review before deleting"

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

import Foundation

struct Email: Identifiable, Codable {
    let id: String
    let threadId: String
    let from: String
    let to: String
    let subject: String
    let snippet: String
    let body: String
    let date: Date
    var isRead: Bool
    var isStarred: Bool
    var labels: [String]

    // Threading headers (for Reply)
    var messageId: String?      // Header: Message-ID
    var inReplyTo: String?      // Header: In-Reply-To
    var references: String?     // Header: References

    // Attachments
    var attachments: [Attachment]

    // AI Analysis
    var analysis: EmailAnalysis?

    var hasAttachments: Bool { !attachments.isEmpty }
    var attachmentCount: Int { attachments.count }

    var fromName: String {
        if let match = from.firstMatch(of: /^([^<]+)\s*</) {
            return String(match.1).trimmingCharacters(in: .whitespaces)
        }
        return from.components(separatedBy: "@").first ?? from
    }

    var fromEmail: String {
        if let match = from.firstMatch(of: /<([^>]+)>/) {
            return String(match.1)
        }
        return from
    }
}

struct EmailAnalysis: Codable {
    let summary: String
    let sentiment: Sentiment
    let priority: Priority
    let category: Category
    let actionItems: [String]
    let keyTopics: [String]
    let suggestedReply: String?
    let analyzedAt: Date

    // Phase 4: Advanced AI fields
    var confidence: Double
    var isSpam: Bool
    var phishingRisk: PhishingRisk

    enum PhishingRisk: String, Codable, CaseIterable {
        case none, low, medium, high

        var color: String {
            switch self {
            case .none: return "green"
            case .low: return "yellow"
            case .medium: return "orange"
            case .high: return "red"
            }
        }

        var icon: String {
            switch self {
            case .none: return "checkmark.shield"
            case .low: return "shield"
            case .medium: return "exclamationmark.shield"
            case .high: return "xmark.shield.fill"
            }
        }
    }

    enum DeleteRecommendation: String, Codable, CaseIterable {
        case safeToDelete   // importanceScore < 30
        case review         // importanceScore 30-60
        case keep           // importanceScore > 60
        case suspicious     // phishingRisk >= medium OR isSpam

        var icon: String {
            switch self {
            case .safeToDelete: return "trash.circle.fill"
            case .review: return "eye.circle.fill"
            case .keep: return "checkmark.circle.fill"
            case .suspicious: return "exclamationmark.shield.fill"
            }
        }

        var description: String {
            switch self {
            case .safeToDelete: return "Safe to delete"
            case .review: return "Review before deleting"


     1→import Foundation
     2→
     3→// MARK: - AI Model Types
     4→
     5→struct AIModel: Identifiable, Hashable {
     6→    let id: String
     7→    let name: String
     8→    let ownedBy: String?
     9→
    10→    var displayName: String {
    11→        name.isEmpty ? id : name
    12→    }
    13→}
    14→
    15→struct ModelsResponse: Decodable {
    16→    let data: [ModelData]
    17→
    18→    struct ModelData: Decodable {
    19→        let id: String
    20→        let owned_by: String?
    21→    }
    22→}
    23→
    24→// MARK: - Reply Types
    25→
    26→enum ReplyTone: String, CaseIterable {
    27→    case professional = "Professional"
    28→    case friendly = "Friendly"
    29→    case formal = "Formal"
    30→    case brief = "Brief"
    31→
    32→    var icon: String {
    33→        switch self {
    34→        case .professional: return "briefcase"
    35→        case .friendly: return "face.smiling"
    36→        case .formal: return "building.columns"
    37→        case .brief: return "bolt"
    38→        }
    39→    }
    40→
    41→    var description: String {
    42→        switch self {
    43→        case .professional: return "Business appropriate"
    44→        case .friendly: return "Warm and personable"
    45→        case .formal: return "Very formal tone"
    46→        case .brief: return "Short and direct"
    47→        }
    48→    }
    49→}
    50→
    51→struct ReplyOption: Identifiable {
    52→    let id = UUID()
    53→    let tone: ReplyTone
    54→    let content: String
    55→}
    56→
    57→// MARK: - Inbox Summary
    58→
    59→struct InboxSummary: Codable {
    60→    let overview: String
    61→    let urgentCount: Int
    62→    let actionRequired: [String]
    63→    let topSenders: [String]
    64→    let categories: [String: Int]
    65→    let suggestions: [String]
    66→}
    67→
    68→// MARK: - AI Error Types
    69→
    70→enum AIError: Error, LocalizedError {
    71→    case invalidResponse
    72→    case serverError(Int)
    73→    case emptyResponse
    74→    case parseError
    75→    case connectionFailed
    76→    case analysisError(String)
    77→    case notConfigured(String)
    78→    case invalidURL(String)
    79→    case noInternet
    80→    case timeout
    81→    case serverUnavailable(String)
    82→    case networkError(String)
    83→    case unauthorized(String)
    84→    case forbidden(String)
    85→    case rateLimited(String)
    86→    case apiError(String)
    87→
    88→    var errorDescription: String? {
    89→        switch self {
    90→        case .invalidResponse: return "Invalid response from AI server"
    91→        case .serverError(let code): return "Server error: \(code)"
    92→        case .emptyResponse: return "Empty response from AI"
    93→        case .parseError: return "Failed to parse AI response"
    94→        case .connectionFailed: return "Cannot connect to AI server"
    95→        case .analysisError(let message): return "Analysis error: \(message)"
    96→        case .notConfigured(let message): return message
    97→        case .invalidURL(let message): return message
    98→        case .noInternet: return "No internet connection"
    99→        case .timeout: return "Request timed out"
   100→        case .serverUnavailable(let url): return "AI server unavailable: \(url)"
   101→        case .networkError(let message): return "Network error: \(message)"
   102→        case .unauthorized(let message): return message
   103→        case .forbidden(let message): return message
   104→        case .rateLimited(let message): return message
   105→        case .apiError(let message): return "API error: \(message)"
   106→        }
   107→    }
   108→
   109→    var isRetryable: Bool {
   110→        switch self {
   111→        case .timeout, .serverError, .rateLimited, .serverUnavailable, .networkError:
   112→            return true
   113→        default:
   114→            return false
   115→        }
   116→    }
   117→
   118→    var requiresConfiguration: Bool {
   119→        switch self {
   120→        case .notConfigured, .unauthorized, .forbidden, .invalidURL:
   121→            return true
   122→        default:
   123→            return false
   124→        }
   125→    }
   126→}
   127→
   128→// MARK: - API Error Response
   129→
   130→struct APIErrorResponse: Decodable {
   131→    let error: APIErrorDetail
   132→
   133→    struct APIErrorDetail: Decodable {
   134→        let message: String
   135→        let type: String?
   136→        let code: String?
   137→    }
   138→}
   139→
   140→// MARK: - Chat Completion Models
   141→
   142→struct ChatCompletionRequest: Encodable {
   143→    let model: String
   144→    let messages: [ChatMessage]
   145→    let maxTokens: Int
   146→    let temperature: Double
   147→
   148→    enum CodingKeys: String, CodingKey {
   149→        case model, messages, temperature
   150→        case maxTokens = "max_tokens"

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

import Foundation

// MARK: - AI Model Types

struct AIModel: Identifiable, Hashable {
    let id: String
    let name: String
    let ownedBy: String?

    var displayName: String {
        name.isEmpty ? id : name
    }
}

struct ModelsResponse: Decodable {
    let data: [ModelData]

    struct ModelData: Decodable {
        let id: String
        let owned_by: String?
    }
}

// MARK: - Reply Types

enum ReplyTone: String, CaseIterable {
    case professional = "Professional"
    case friendly = "Friendly"
    case formal = "Formal"
    case brief = "Brief"

    var icon: String {
        switch self {
        case .professional: return "briefcase"
        case .friendly: return "face.smiling"
        case .formal: return "building.columns"
        case .brief: return "bolt"
        }
    }

    var description: String {
        switch self {
        case .professional: return "Business appropriate"
        case .friendly: return "Warm and personable"
        case .formal: return "Very formal tone"
        case .brief: return "Short and direct"
        }
    }
}

struct ReplyOption: Identifiable {
    let id = UUID()
    let tone: ReplyTone
    let content: String
}

// MARK: - Inbox Summary

struct InboxSummary: Codable {
    let overview: String
    let urgentCount: Int
    let actionRequired: [String]
    let topSenders: [String]
    let categories: [String: Int]
    let suggestions: [String]
}

// MARK: - AI Error Types

enum AIError: Error, LocalizedError {
    case invalidResponse
    case serverError(Int)
    case emptyResponse
    case parseError
    case connectionFailed
    case analysisError(String)
    case notConfigured(String)
    case invalidURL(String)
    case noInternet
    case timeout
    case serverUnavailable(String)
    case networkError(String)
    case unauthorized(String)
    case forbidden(String)
    case rateLimited(String)
    case apiError(String)

    var errorDescription: String? {
        switch self {
        case .invalidResponse: return "Invalid response from AI server"
        case .serverError(let code): return "Server error: \(code)"
        case .emptyResponse: return "Empty response from AI"
        case .parseError: return "Failed to parse AI response"
        case .connectionFailed: return "Cannot connect to AI server"
        case .analysisError(let message): return "Analysis error: \(message)"
        case .notConfigured(let message): return message
        case .invalidURL(let message): return message
        case .noInternet: return "No internet connection"
        case .timeout: return "Request timed out"
        case .serverUnavailable(let url): return "AI server unavailable: \(url)"
        case .networkError(let message): return "Network error: \(message)"
        case .unauthorized(let message): return message
        case .forbidden(let message): return message
        case .rateLimited(let message): return message
        case .apiError(let message): return "API error: \(message)"
        }
    }

    var isRetryable: Bool {
        switch self {
        case .timeout, .serverError, .rateLimited, .serverUnavailable, .networkError:
            return true
        default:
            return false
        }
    }

    var requiresConfiguration: Bool {
        switch self {
        case .notConfigured, .unauthorized, .forbidden, .invalidURL:
            return true
        default:
            return false
        }
    }
}

// MARK: - API Error Response

struct APIErrorResponse: Decodable {
    let error: APIErrorDetail

    struct APIErrorDetail: Decodable {
        let message: String
        let type: String?
        let code: String?
    }
}

// MARK: - Chat Completion Models

struct ChatCompletionRequest: Encodable {
    let model: String
    let messages: [ChatMessage]
    let maxTokens: Int
    let temperature: Double

    enum CodingKeys: String, CodingKey {
        case model, messages, temperature
        case maxTokens = "max_tokens"


Perfect! Now let me check one more file to understand the data models:


Bash

grep -n "DeleteRecommendation\|importanceScore\|hasActionItems\|CleanupStep\|CleanupStats" /Users/neog/Apps/iOS/GmailAI-iOS/Models/Email.swift | head -40

Search for cleanup-related model definitions


     1→import SwiftUI
     2→
     3→// MARK: - Cleanup Step Enum
     4→
     5→enum CleanupStep: Int, CaseIterable {
     6→    case safeToDelete = 0
     7→    case suspicious = 1
     8→    case review = 2
     9→
    10→    var title: String {
    11→        switch self {
    12→        case .safeToDelete: return "Safe to Delete"
    13→        case .suspicious: return "Suspicious Emails"
    14→        case .review: return "Review These"
    15→        }
    16→    }
    17→
    18→    var description: String {
    19→        switch self {
    20→        case .safeToDelete: return "Low importance emails that can be safely removed"
    21→        case .suspicious: return "Potential spam or phishing - proceed with caution"
    22→        case .review: return "Medium importance - review before deciding"
    23→        }
    24→    }
    25→
    26→    var icon: String {
    27→        switch self {
    28→        case .safeToDelete: return "trash.circle.fill"
    29→        case .suspicious: return "exclamationmark.shield.fill"
    30→        case .review: return "eye.circle.fill"
    31→        }
    32→    }
    33→
    34→    var color: Color {
    35→        switch self {
    36→        case .safeToDelete: return .green
    37→        case .suspicious: return .red
    38→        case .review: return .yellow
    39→        }
    40→    }
    41→}
    42→
    43→// MARK: - Progress Indicator
    44→
    45→struct CleanupProgressIndicator: View {
    46→    let currentStep: CleanupStep
    47→
    48→    var body: some View {
    49→        HStack(spacing: 0) {
    50→            ForEach(CleanupStep.allCases, id: \.rawValue) { step in
    51→                HStack(spacing: 0) {
    52→                    // Circle
    53→                    Circle()
    54→                        .fill(step.rawValue <= currentStep.rawValue ? step.color : Color.secondary.opacity(0.3))
    55→                        .frame(width: 32, height: 32)
    56→                        .overlay {
    57→                            if step.rawValue < currentStep.rawValue {
    58→                                Image(systemName: "checkmark")
    59→                                    .font(.caption.bold())
    60→                                    .foregroundStyle(.white)
    61→                            } else {
    62→                                Text("\(step.rawValue + 1)")
    63→                                    .font(.caption.bold())
    64→                                    .foregroundStyle(step.rawValue <= currentStep.rawValue ? .white : .secondary)
    65→                            }
    66→                        }
    67→
    68→                    // Line (except for last)
    69→                    if step.rawValue < CleanupStep.allCases.count - 1 {
    70→                        Rectangle()
    71→                            .fill(step.rawValue < currentStep.rawValue ? step.color : Color.secondary.opacity(0.3))
    72→                            .frame(height: 3)
    73→                    }
    74→                }
    75→            }
    76→        }
    77→        .padding(.horizontal)
    78→    }
    79→}
    80→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

import SwiftUI

// MARK: - Cleanup Step Enum

enum CleanupStep: Int, CaseIterable {
    case safeToDelete = 0
    case suspicious = 1
    case review = 2

    var title: String {
        switch self {
        case .safeToDelete: return "Safe to Delete"
        case .suspicious: return "Suspicious Emails"
        case .review: return "Review These"
        }
    }

    var description: String {
        switch self {
        case .safeToDelete: return "Low importance emails that can be safely removed"
        case .suspicious: return "Potential spam or phishing - proceed with caution"
        case .review: return "Medium importance - review before deciding"
        }
    }

    var icon: String {
        switch self {
        case .safeToDelete: return "trash.circle.fill"
        case .suspicious: return "exclamationmark.shield.fill"
        case .review: return "eye.circle.fill"
        }
    }

    var color: Color {
        switch self {
        case .safeToDelete: return .green
        case .suspicious: return .red
        case .review: return .yellow
        }
    }
}

// MARK: - Progress Indicator

struct CleanupProgressIndicator: View {
    let currentStep: CleanupStep

    var body: some View {
        HStack(spacing: 0) {
            ForEach(CleanupStep.allCases, id: \.rawValue) { step in
                HStack(spacing: 0) {
                    // Circle
                    Circle()
                        .fill(step.rawValue <= currentStep.rawValue ? step.color : Color.secondary.opacity(0.3))
                        .frame(width: 32, height: 32)
                        .overlay {
                            if step.rawValue < currentStep.rawValue {
                                Image(systemName: "checkmark")
                                    .font(.caption.bold())
                                    .foregroundStyle(.white)
                            } else {
                                Text("\(step.rawValue + 1)")
                                    .font(.caption.bold())
                                    .foregroundStyle(step.rawValue <= currentStep.rawValue ? .white : .secondary)
                            }
                        }

                    // Line (except for last)
                    if step.rawValue < CleanupStep.allCases.count - 1 {
                        Rectangle()
                            .fill(step.rawValue < currentStep.rawValue ? step.color : Color.secondary.opacity(0.3))
                            .frame(height: 3)
                    }
                }
            }
        }
        .padding(.horizontal)
    }
}


82:    enum DeleteRecommendation: String, Codable, CaseIterable {
83:        case safeToDelete   // importanceScore < 30
84:        case review         // importanceScore 30-60
85:        case keep           // importanceScore > 60
108:    var importanceScore: Int
109:    var deleteRecommendation: DeleteRecommendation
121:    var hasActionItems: Bool {
126:        switch importanceScore {


Excellent! Now I have a comprehensive understanding of the codebase. Let me create a detailed report.

## GmailAI-iOS Codebase Exploration Report

### 1. AI Analysis Architecture

#### **AIAnalysisService.swift** - Core Analysis Engine
- **Configuration**:
  - Uses LM Studio via Tailscale (default: `mac-studio-de-leandro.tail0360aa.ts.net`)
  - API key stored securely in Keychain
  - Supports configurable server URL and model selection
  - Cached JSONEncoder/JSONDecoder for performance

- **Single Email Analysis** (`analyzeEmail()`)
  - Extracts key content from email (intelligent chunking)
  - Builds analysis prompt via `AIPromptBuilder`
  - Sends to LM Studio `/v1/chat/completions` endpoint
  - Parses response using `AIResponseParser`
  - Returns `EmailAnalysis` with priority, sentiment, category, action items

- **Additional Analysis Methods**:
  - `generateReplyOptions()` - Generates 3 tones (professional, friendly, brief)
  - `summarizeThread()` - Thread conversation summary
  - `generateInboxSummary()` - Analytics on first 20 emails
  - `checkHealth()` - Connection verification
  - `fetchAvailableModels()` - Lists LM Studio models

#### **AIServiceBatch.swift** - Parallel Analysis (5-6x Faster)

**Two batch analysis modes**:

1. **Basic Batch** (`analyzeEmailsBatch`)
   - Analyzes multiple emails in parallel
   - Configurable concurrency limit (default: `maxConcurrentAnalysis` from settings)
   - Progress callback: `(Double) -> Void` (0.0 to 1.0)
   - Returns: `[String: EmailAnalysis]` (emailId → analysis map)
   - Uses actor pattern (`AnalysisResultsActor`) for thread-safe results

2. **Event-Based Batch** (`analyzeEmailsBatchWithEvents`)
   - Real-time progress with detailed events
   - Supports pause/resume with cancellation detection
   - Emits `AnalysisEvent` callbacks:
     - `started(email)` - Analysis began
     - `completed(email, analysis, duration)` - Completed with timing
     - `failed(email, error, duration)` - Failed with error message
     - `progress(completed, total)` - Progress update
   - Returns: `(results, remainingIds)` - Completed analyses and unfinished emails
   - Thread-safe using `AnalysisResultsActor` and `RemainingEmailsActor`

**Performance**:
- Uses `withTaskGroup` for concurrent execution
- Exponential backoff retry with jitter
- Logging at each step for diagnostics

---

### 2. Email Actions

#### **GmailServiceActions.swift** - Gmail API Actions

**Individual Actions**:
- `markAsRead(emailId)` - Remove UNREAD label
- `markAsUnread(emailId)` - Add UNREAD label
- `toggleStar(emailId, starred)` - Add/remove STARRED label
- `archiveEmail(emailId)` - Remove INBOX label
- `unarchiveEmail(emailId)` - Add INBOX label back
- `trashEmail(emailId)` - Move to TRASH
- `deleteEmailPermanently(emailId)` - Permanent deletion

**Batch Operations**:
- `batchModifyLabels(emailIds, addLabels, removeLabels)` - Single API call for multiple emails
  - Handles token refresh on 401 (automatic retry)
  - Returns on success (200 or 204)
  - Rate limiting handling (429 errors)

**Implementation Pattern**:
```swift
// Single email: POST /messages/{id}/modify with label changes
// Batch: POST /messages/batchModify with ids array and label arrays
// All use Bearer token auth with retry logic
```

#### **EmailStoreActions.swift** - Local State Management

**Single Email Actions** (updates local array + cache):
- `markAsRead()`, `markAsUnread()` - Update isRead flag
- `archiveEmail()`, `unarchiveEmail()` - Add/remove from local array
- `deleteEmail()`, `deleteEmailPermanently()` - Remove from array
- `toggleStar()` - Toggle isStarred flag
- `sendEmail()` - Send and optionally refresh thread

**Batch Operations** (efficient):
- `markEmailsAsRead()`, `markEmailsAsUnread()` - Update multiple at once
- `archiveEmails()` - Batch archive using Set for fast lookup
- `deleteEmailsBatch()` - Batch delete (moves to trash)
- `deleteEmails()` - Sequential deletion fallback

**Pattern**:
```swift
// 1. Call Gmail API (gmailService)
// 2. Update local emails array
// 3. Save to cache (cacheService.saveEmails)
// 4. Update stats
// 5. Return success/count
```

---

### 3. Smart Folders & Cleanup System

#### **EmailStoreFiltering.swift** - Smart Folder Definitions

**Smart Folders** (computed properties, AI-driven):

1. **requiresActionEmails**
   - Criteria: `(priority == .high OR .critical) AND hasActionItems AND !isRead`
   - Use case: Urgent, actionable unread emails

2. **safeToDeleteEmails**
   - Criteria: `deleteRecommendation == .safeToDelete`
   - Basis: importanceScore < 30, not suspicious
   - UI: Sorted by importance (lowest first)

3. **reviewEmails**
   - Criteria: `deleteRecommendation == .review`
   - Basis: importanceScore 30-60, needs human judgment
   - UI: Most recent first

4. **suspiciousEmails**
   - Criteria: `deleteRecommendation == .suspicious`
   - Basis: phishingRisk >= medium OR isSpam
   - Warning: Proceed with caution

5. **oldUnreadEmails**
   - Criteria: `!isRead AND date < 7 days ago`
   - Use case: Stale, unread emails

6. **importantEmails**
   - Criteria: `deleteRecommendation == .keep`
   - Basis: importanceScore > 60
   - UI: Sorted by importance (highest first)

**Cleanup Statistics**:
```swift
struct CleanupStats {
    let safeToDeleteCount: Int
    let reviewCount: Int
    let suspiciousCount: Int
    let oldUnreadCount: Int
    let requiresActionCount: Int
    let importantCount: Int

    var totalCleanupCandidates: Int // safeToDelete + suspicious + review
}
```

---

### 4. Cleanup UI Flow

#### **SmartFoldersView.swift** - Entry Point
- Summary card with cleanup stats
- Organized into sections:
  - **Action Required** - requiresAction folder
  - **Cleanup Candidates** - safeToDelete, suspicious, review
  - **Review** - important, oldUnread
- CTA: "Start Guided Cleanup" button

#### **CleanupReviewView.swift** - Step-by-Step Wizard

**3-Step Process**:
1. **Safe to Delete** - Low importance emails
2. **Suspicious** - Potential spam/phishing
3. **Review** - Medium importance (human judgment)

**UI Components** (from CleanupComponents.swift):
- `CleanupProgressIndicator` - Visual progress bar (3 circles + lines)
- `CleanupEmailRow` - Checkbox + email preview
- `CleanupSummaryCard` - Stats overview
- `CleanupStatRow` - Single stat display

**Workflow**:
1. Select emails via checkboxes (or "Select All")
2. Click "Delete Selected"
3. Confirm in alert dialog
4. Auto-advance if step empties
5. Skip to next step or finish
6. Show completion screen with stats

**Features**:
- Selection tracking: `@State private var selectedEmails: Set<String>`
- Batch delete using `emailStore.deleteEmailsBatch()`
- Deletion counter for feedback
- Pause at empty steps
- Completion animation

---

### 5. Data Models

#### **EmailAnalysis** (in Email.swift)
```swift
struct EmailAnalysis: Codable {
    // Base analysis
    let summary: String
    let sentiment: Sentiment
    let priority: Priority  // low/medium/high/critical
    let category: Category
    let actionItems: [String]
    let keyTopics: [String]
    let suggestedReply: String?
    let analyzedAt: Date

    // Advanced fields
    var confidence: Double
    var isSpam: Bool
    var phishingRisk: PhishingRisk  // none/low/medium/high

    // Cleanup scoring
    var importanceScore: Int  // 0-100
    var deleteRecommendation: DeleteRecommendation

    var hasActionItems: Bool { !actionItems.isEmpty }
}
```

#### **DeleteRecommendation** (Enum)
```swift
enum DeleteRecommendation: String {
    case safeToDelete   // importanceScore < 30
    case review         // importanceScore 30-60
    case keep           // importanceScore > 60
    case suspicious     // phishingRisk >= medium OR isSpam
}
```

---

### 6. Batch Analysis Integration in EmailStore

#### **EmailStoreAnalysis.swift** Extension

**Three Analysis Methods**:

1. **Single Analysis** (`analyzeEmail()`)
   - Updates single email with analysis
   - Saves to cache immediately

2. **Batch Analysis** (`analyzeAllEmailsBatch()`)
   - Finds all unanalyzed emails
   - Calls `aiService.analyzeEmailsBatch()`
   - Updates all in memory
   - Single batch cache save
   - Progress callback: `(Double) -> Void`

3. **Live Analysis** (`analyzeAllEmailsWithLiveUpdates()`)
   - Event-driven with pause/resume
   - Calls `aiService.analyzeEmailsBatchWithEvents()`
   - Handles `AnalysisEvent` callbacks
   - Stores analysis task for cancellation
   - Logs to `analysisState` in real-time
   - Throttled stats updates (max every 100ms)

**Control Methods**:
- `pauseAnalysis()` - Cancel current task (auto-detects pause)
- `resumeAnalysis()` - Continue from paused state
- `cancelAnalysis()` - Full reset

---

### 7. Key Patterns for Implementation

#### **Batch AI Analysis Pattern**
```swift
// In AIServiceBatch.swift
1. Guard against empty input
2. Use actor pattern for thread-safe results collection
3. Use withTaskGroup with concurrency limit
4. Track in-flight tasks (inFlight counter)
5. Process results as they complete (queue.next())
6. Log metrics (duration, success rate)
7. Return results dictionary or (results, remaining) tuple
```

#### **Email Action Pattern**
```swift
// In EmailStoreActions.swift
1. Extract emails to modify (Set for fast lookup)
2. Call gmailService.batchModifyLabels() or equivalent
3. Update local emails array
4. Save to cache
5. Call updateStats()
6. Return count/success
```

#### **Smart Folder Pattern**
```swift
// In EmailStoreFiltering.swift
1. Filter emails by analysis criteria
2. Sort appropriately (importance, date)
3. Expose as computed properties
4. Aggregate stats via CleanupStats
```

#### **Cleanup Review Pattern**
```swift
// In CleanupReviewView.swift
1. Enum-driven steps (CleanupStep)
2. Selection tracking via Set<String>
3. Batch deletion with progress state
4. Auto-advance to next step
5. Multi-step form with completion screen
```

---

### 8. Critical Files Location

| File | Path | Purpose |
|------|------|---------|
| AIAnalysisService.swift | Services/ | Core AI analysis, reply generation |
| AIServiceBatch.swift | Services/ | Parallel batch analysis with events |
| GmailServiceActions.swift | Services/ | Gmail API actions (modify, delete) |
| EmailStoreActions.swift | Models/ | Local state + cache updates |
| EmailStoreFiltering.swift | Models/ | Smart folders + cleanup stats |
| EmailStoreAnalysis.swift | Models/ | Batch analysis orchestration |
| Email.swift | Models/ | EmailAnalysis data structure |
| CleanupReviewView.swift | Views/ | Multi-step cleanup wizard |
| SmartFoldersView.swift | Views/ | Smart folder navigation |
| CleanupComponents.swift | Views/ | Cleanup UI components |

---

### 9. External API Integration Points

**LM Studio Endpoints** (via AIAnalysisService):
- `POST /v1/chat/completions` - Analysis, reply generation, summaries
- `GET /v1/models` - Available models list

**Gmail API Endpoints** (via GmailServiceActions):
- `POST /messages/{id}/modify` - Individual label changes
- `POST /messages/batchModify` - Batch label changes
- `POST /messages/{id}/trash` - Move to trash
- `DELETE /messages/{id}` - Permanent delete

---

### 10. Performance Characteristics

- **Parallel Analysis**: 5-6x faster than sequential (5 concurrent requests)
- **Batch Operations**: Single API call for multiple emails (vs N calls)
- **Caching**: Single disk write per batch (vs N writes)
- **Stats Updates**: Throttled to max every 100ms during live analysis
- **Exponential Backoff**: Retry delays with jitter to prevent thundering herd

This architecture supports efficient inbox cleanup through AI-driven recommendation + user review + batch deletion operations.