Design a macOS SwiftUI app called "BRDataMiner" for mining Brazilian data from a 170GB text file on an external SSD.

## User Requirements:
- **Platform:** macOS native SwiftUI app
- **Input:** 170GB .txt file on external 500GB SSD (USB connected to MacBook Air M4)
- **Output:** Structured JSON files

## Data Types to Extract:
1. **Emails** - All emails, highlight .br domains
2. **Domains .br** - .com.br, .org.br, .net.br, .gov.br, etc.
3. **CPF** - Format: ###.###.###-## (Brazilian personal ID)
4. **CNPJ** - Format: ##.###.###/####-## (Brazilian company ID)
5. **Phone numbers BR** - Cellular and landline with DDD (+55)
6. **URLs** - HTTP/HTTPS links

## Critical Technical Constraints:
- **170GB file CANNOT be loaded into memory** - must stream line by line
- Must handle external SSD access via USB
- Need progress tracking with pause/resume capability
- Checkpoint system to resume if interrupted
- Leverage M4 chip for performance (concurrent processing)

## Existing Patterns to Follow (from workspace):
- DomainChecker at `/Users/neog/Projects/tools/DomainChecker/` - has ImportService with batch processing, progress callbacks, checkpoint/resume
- Uses Actor pattern for concurrent validation
- Batch size of 1000 items
- Export to JSON/CSV with filtering

## Architecture Requirements:
1. **StreamingFileReader** - Read file line by line without loading into memory
2. **RegexExtractor** - Swift Regex patterns for each data type
3. **DataValidator** - Validate CPF/CNPJ checksums, email format
4. **ProgressTracker** - Track bytes read, estimated time, pause/resume
5. **CheckpointManager** - Save position to resume after interruption
6. **ExportService** - Write extracted data to JSON files

## Recommended Regex Patterns:
```swift
// CPF: XXX.XXX.XXX-XX or XXXXXXXXXXX
let cpfPattern = #"\d{3}\.?\d{3}\.?\d{3}-?\d{2}"#

// CNPJ: XX.XXX.XXX/XXXX-XX
let cnpjPattern = #"\d{2}\.?\d{3}\.?\d{3}/?\d{4}-?\d{2}"#

// Email
let emailPattern = #"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"#

// BR Phone: (XX) XXXXX-XXXX or +55 XX XXXXX-XXXX
let phonePattern = #"(?:\+55\s?)?\(?\d{2}\)?\s?\d{4,5}-?\d{4}"#

// .br domain
let brDomainPattern = #"[a-zA-Z0-9.-]+\.(?:com|org|net|gov|edu|mil)\.br"#

// URL
let urlPattern = #"https?://[^\s<>\"\']+"#
```

## UI Requirements:
1. File picker for selecting input file from external SSD
2. Output directory selector
3. Checkboxes to enable/disable each data type extraction
4. Real-time progress bar with:
   - Bytes processed / Total bytes
   - Lines processed
   - Items found per category
   - Estimated time remaining
5. Pause/Resume/Cancel buttons
6. Export button when complete
7. Statistics dashboard

## Output JSON Structure:
```json
{
  "metadata": {
    "source_file": "path/to/file.txt",
    "processed_at": "2025-12-28T...",
    "total_lines": 1000000,
    "processing_time_seconds": 3600
  },
  "statistics": {
    "emails_br": 50000,
    "emails_other": 100000,
    "domains_br": 25000,
    "cpf": 10000,
    "cnpj": 5000,
    "phones": 8000,
    "urls": 15000
  },
  "data": {
    "emails": [...],
    "domains": [...],
    "cpf": [...],
    "cnpj": [...],
    "phones": [...],
    "urls": [...]
  }
}
```

Design a complete implementation plan with:
1. Project structure (files and folders)
2. Data models (Swift structs)
3. Core services implementation approach
4. UI views structure
5. Step-by-step implementation order
6. Performance optimization strategies for 170GB file


     1→import Foundation
     2→import CoreData
     3→
     4→/// Service for importing domains from files
     5→actor ImportService {
     6→    static let shared = ImportService()
     7→
     8→    private let batchSize = 1000
     9→
    10→    /// Import domains from a text file
    11→    /// - Parameters:
    12→    ///   - fileURL: URL of the file to import
    13→    ///   - context: CoreData context for saving
    14→    ///   - onProgress: Progress callback (0.0 - 1.0)
    15→    /// - Returns: Number of domains imported
    16→    func importDomains(
    17→        from fileURL: URL,
    18→        context: NSManagedObjectContext,
    19→        session: ValidationSessionEntity,
    20→        onProgress: @escaping (Double) -> Void
    21→    ) async throws -> Int {
    22→        // Read file content
    23→        let content = try String(contentsOf: fileURL, encoding: .utf8)
    24→        let lines = content.components(separatedBy: .newlines)
    25→
    26→        // Parse and clean domains
    27→        var domains: [(lineNumber: Int, domain: String, tld: String)] = []
    28→        var seenDomains = Set<String>() // Deduplication
    29→
    30→        for (index, line) in lines.enumerated() {
    31→            let cleaned = cleanDomain(line)
    32→            if !cleaned.isEmpty && !seenDomains.contains(cleaned) {
    33→                seenDomains.insert(cleaned)
    34→                let tld = extractTLD(from: cleaned)
    35→                domains.append((index + 1, cleaned, tld))
    36→            }
    37→        }
    38→
    39→        let totalDomains = domains.count
    40→        var importedCount = 0
    41→
    42→        // Batch insert
    43→        for batchStart in stride(from: 0, to: domains.count, by: batchSize) {
    44→            let batchEnd = min(batchStart + batchSize, domains.count)
    45→            let batch = Array(domains[batchStart..<batchEnd])
    46→
    47→            try await context.perform {
    48→                for (lineNumber, domain, tld) in batch {
    49→                    let entity = DomainEntity(context: context)
    50→                    entity.id = UUID()
    51→                    entity.domain = domain
    52→                    entity.status = DomainStatus.pending.rawValue
    53→                    entity.importedAt = Date()
    54→                    entity.lineNumber = Int32(lineNumber)
    55→                    entity.tld = tld
    56→                    entity.retryCount = 0
    57→                    entity.dnsResolved = false
    58→                    entity.session = session
    59→                }
    60→
    61→                try context.save()
    62→            }
    63→
    64→            importedCount += batch.count
    65→
    66→            // Report progress
    67→            let progress = Double(importedCount) / Double(totalDomains)
    68→            await MainActor.run {
    69→                onProgress(progress)
    70→            }
    71→        }
    72→
    73→        return importedCount
    74→    }
    75→
    76→    /// Clean and normalize domain string
    77→    private func cleanDomain(_ input: String) -> String {
    78→        var domain = input.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
    79→
    80→        // Skip empty lines and comments
    81→        if domain.isEmpty || domain.hasPrefix("#") || domain.hasPrefix("//") {
    82→            return ""
    83→        }
    84→
    85→        // Remove protocol
    86→        for prefix in ["https://", "http://", "ftp://", "//"] {
    87→            if domain.hasPrefix(prefix) {
    88→                domain = String(domain.dropFirst(prefix.count))
    89→            }
    90→        }
    91→
    92→        // Remove path (everything after first /)
    93→        if let slashIndex = domain.firstIndex(of: "/") {
    94→            domain = String(domain[..<slashIndex])
    95→        }
    96→
    97→        // Remove port
    98→        if let colonIndex = domain.firstIndex(of: ":") {
    99→            domain = String(domain[..<colonIndex])
   100→        }
   101→
   102→        // Remove query string (shouldn't exist after path removal, but just in case)
   103→        if let queryIndex = domain.firstIndex(of: "?") {
   104→            domain = String(domain[..<queryIndex])
   105→        }
   106→
   107→        // Remove www prefix
   108→        if domain.hasPrefix("www.") {
   109→            domain = String(domain.dropFirst(4))
   110→        }
   111→
   112→        // Trim any remaining whitespace
   113→        domain = domain.trimmingCharacters(in: .whitespaces)
   114→
   115→        // Validation
   116→        guard isValidDomain(domain) else {
   117→            return ""
   118→        }
   119→
   120→        return domain
   121→    }
   122→
   123→    /// Validate domain format
   124→    private func isValidDomain(_ domain: String) -> Bool {
   125→        // Must contain at least one dot
   126→        guard domain.contains(".") else { return false }
   127→
   128→        // Must be at least 4 chars (a.bc)
   129→        guard domain.count >= 4 else { return false }
   130→
   131→        // Must not contain spaces
   132→        guard !domain.contains(" ") else { return false }
   133→
   134→        // Must not contain invalid characters
   135→        let invalidChars = CharacterSet(charactersIn: "!@#$%^&*()+=[]{}|\\;:'\",<>?`~")
   136→        guard domain.unicodeScalars.allSatisfy({ !invalidChars.contains($0) }) else { return false }
   137→
   138→        // Must not be an IP address (simple check)
   139→        let parts = domain.components(separatedBy: ".")
   140→        if parts.count == 4, parts.allSatisfy({ Int($0) != nil }) {
   141→            return false
   142→        }
   143→
   144→        return true
   145→    }
   146→
   147→    /// Extract TLD from domain
   148→    private func extractTLD(from domain: String) -> String {
   149→        let parts = domain.components(separatedBy: ".")
   150→        guard parts.count >= 2 else { return "" }
   151→
   152→        // Handle common multi-part TLDs
   153→        let lastTwo = parts.suffix(2).joined(separator: ".")
   154→        let multiPartTLDs = Set([
   155→            "com.br", "co.uk", "com.au", "co.nz", "co.jp", "com.mx",
   156→            "org.uk", "net.au", "gov.br", "edu.br", "com.ar", "co.kr",
   157→            "co.in", "com.cn", "co.za", "com.sg", "com.hk", "com.tw"
   158→        ])
   159→
   160→        if multiPartTLDs.contains(lastTwo) {
   161→            return ".\(lastTwo)"
   162→        }
   163→
   164→        return ".\(parts.last ?? "")"
   165→    }
   166→
   167→    /// Parse CSV file (domain in first column)
   168→    func importFromCSV(
   169→        from fileURL: URL,
   170→        context: NSManagedObjectContext,
   171→        session: ValidationSessionEntity,
   172→        onProgress: @escaping (Double) -> Void
   173→    ) async throws -> Int {
   174→        let content = try String(contentsOf: fileURL, encoding: .utf8)
   175→        let lines = content.components(separatedBy: .newlines)
   176→
   177→        var domains: [(lineNumber: Int, domain: String, tld: String)] = []
   178→        var seenDomains = Set<String>()
   179→
   180→        // Skip header if present
   181→        let startIndex = lines.first?.lowercased().contains("domain") == true ? 1 : 0
   182→
   183→        for (index, line) in lines.enumerated() where index >= startIndex {
   184→            // Get first column (domain)
   185→            let columns = line.components(separatedBy: [",", ";", "\t"])
   186→            guard let firstColumn = columns.first else { continue }
   187→
   188→            let cleaned = cleanDomain(firstColumn)
   189→            if !cleaned.isEmpty && !seenDomains.contains(cleaned) {
   190→                seenDomains.insert(cleaned)
   191→                let tld = extractTLD(from: cleaned)
   192→                domains.append((index + 1, cleaned, tld))
   193→            }
   194→        }
   195→
   196→        // Same batch insert logic...
   197→        let totalDomains = domains.count
   198→        var importedCount = 0
   199→
   200→        for batchStart in stride(from: 0, to: domains.count, by: batchSize) {
   201→            let batchEnd = min(batchStart + batchSize, domains.count)
   202→            let batch = Array(domains[batchStart..<batchEnd])
   203→
   204→            try await context.perform {
   205→                for (lineNumber, domain, tld) in batch {
   206→                    let entity = DomainEntity(context: context)
   207→                    entity.id = UUID()
   208→                    entity.domain = domain
   209→                    entity.status = DomainStatus.pending.rawValue
   210→                    entity.importedAt = Date()
   211→                    entity.lineNumber = Int32(lineNumber)
   212→                    entity.tld = tld
   213→                    entity.retryCount = 0
   214→                    entity.session = session
   215→                }
   216→
   217→                try context.save()
   218→            }
   219→
   220→            importedCount += batch.count
   221→
   222→            let progress = Double(importedCount) / Double(totalDomains)
   223→            await MainActor.run {
   224→                onProgress(progress)
   225→            }
   226→        }
   227→
   228→        return importedCount
   229→    }
   230→}
   231→

<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
import CoreData

/// Service for importing domains from files
actor ImportService {
    static let shared = ImportService()

    private let batchSize = 1000

    /// Import domains from a text file
    /// - Parameters:
    ///   - fileURL: URL of the file to import
    ///   - context: CoreData context for saving
    ///   - onProgress: Progress callback (0.0 - 1.0)
    /// - Returns: Number of domains imported
    func importDomains(
        from fileURL: URL,
        context: NSManagedObjectContext,
        session: ValidationSessionEntity,
        onProgress: @escaping (Double) -> Void
    ) async throws -> Int {
        // Read file content
        let content = try String(contentsOf: fileURL, encoding: .utf8)
        let lines = content.components(separatedBy: .newlines)

        // Parse and clean domains
        var domains: [(lineNumber: Int, domain: String, tld: String)] = []
        var seenDomains = Set<String>() // Deduplication

        for (index, line) in lines.enumerated() {
            let cleaned = cleanDomain(line)
            if !cleaned.isEmpty && !seenDomains.contains(cleaned) {
                seenDomains.insert(cleaned)
                let tld = extractTLD(from: cleaned)
                domains.append((index + 1, cleaned, tld))
            }
        }

        let totalDomains = domains.count
        var importedCount = 0

        // Batch insert
        for batchStart in stride(from: 0, to: domains.count, by: batchSize) {
            let batchEnd = min(batchStart + batchSize, domains.count)
            let batch = Array(domains[batchStart..<batchEnd])

            try await context.perform {
                for (lineNumber, domain, tld) in batch {
                    let entity = DomainEntity(context: context)
                    entity.id = UUID()
                    entity.domain = domain
                    entity.status = DomainStatus.pending.rawValue
                    entity.importedAt = Date()
                    entity.lineNumber = Int32(lineNumber)
                    entity.tld = tld
                    entity.retryCount = 0
                    entity.dnsResolved = false
                    entity.session = session
                }

                try context.save()
            }

            importedCount += batch.count

            // Report progress
            let progress = Double(importedCount) / Double(totalDomains)
            await MainActor.run {
                onProgress(progress)
            }
        }

        return importedCount
    }

    /// Clean and normalize domain string
    private func cleanDomain(_ input: String) -> String {
        var domain = input.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()

        // Skip empty lines and comments
        if domain.isEmpty || domain.hasPrefix("#") || domain.hasPrefix("//") {
            return ""
        }

        // Remove protocol
        for prefix in ["https://", "http://", "ftp://", "//"] {
            if domain.hasPrefix(prefix) {
                domain = String(domain.dropFirst(prefix.count))
            }
        }

        // Remove path (everything after first /)
        if let slashIndex = domain.firstIndex(of: "/") {
            domain = String(domain[..<slashIndex])
        }

        // Remove port
        if let colonIndex = domain.firstIndex(of: ":") {
            domain = String(domain[..<colonIndex])
        }

        // Remove query string (shouldn't exist after path removal, but just in case)
        if let queryIndex = domain.firstIndex(of: "?") {
            domain = String(domain[..<queryIndex])
        }

        // Remove www prefix
        if domain.hasPrefix("www.") {
            domain = String(domain.dropFirst(4))
        }

        // Trim any remaining whitespace
        domain = domain.trimmingCharacters(in: .whitespaces)

        // Validation
        guard isValidDomain(domain) else {
            return ""
        }

        return domain
    }

    /// Validate domain format
    private func isValidDomain(_ domain: String) -> Bool {
        // Must contain at least one dot
        guard domain.contains(".") else { return false }

        // Must be at least 4 chars (a.bc)
        guard domain.count >= 4 else { return false }

        // Must not contain spaces
        guard !domain.contains(" ") else { return false }

        // Must not contain invalid characters
        let invalidChars = CharacterSet(charactersIn: "!@#$%^&*()+=[]{}|\\;:'\",<>?`~")
        guard domain.unicodeScalars.allSatisfy({ !invalidChars.contains($0) }) else { return false }

        // Must not be an IP address (simple check)
        let parts = domain.components(separatedBy: ".")
        if parts.count == 4, parts.allSatisfy({ Int($0) != nil }) {
            return false
        }

        return true
    }

    /// Extract TLD from domain
    private func extractTLD(from domain: String) -> String {
        let parts = domain.components(separatedBy: ".")
        guard parts.count >= 2 else { return "" }

        // Handle common multi-part TLDs
        let lastTwo = parts.suffix(2).joined(separator: ".")
        let multiPartTLDs = Set([
            "com.br", "co.uk", "com.au", "co.nz", "co.jp", "com.mx",
            "org.uk", "net.au", "gov.br", "edu.br", "com.ar", "co.kr",
            "co.in", "com.cn", "co.za", "com.sg", "com.hk", "com.tw"
        ])

        if multiPartTLDs.contains(lastTwo) {
            return ".\(lastTwo)"
        }

        return ".\(parts.last ?? "")"
    }

    /// Parse CSV file (domain in first column)
    func importFromCSV(
        from fileURL: URL,
        context: NSManagedObjectContext,
        session: ValidationSessionEntity,
        onProgress: @escaping (Double) -> Void
    ) async throws -> Int {
        let content = try String(contentsOf: fileURL, encoding: .utf8)
        let lines = content.components(separatedBy: .newlines)

        var domains: [(lineNumber: Int, domain: String, tld: String)] = []
        var seenDomains = Set<String>()

        // Skip header if present
        let startIndex = lines.first?.lowercased().contains("domain") == true ? 1 : 0

        for (index, line) in lines.enumerated() where index >= startIndex {
            // Get first column (domain)
            let columns = line.components(separatedBy: [",", ";", "\t"])
            guard let firstColumn = columns.first else { continue }

            let cleaned = cleanDomain(firstColumn)
            if !cleaned.isEmpty && !seenDomains.contains(cleaned) {
                seenDomains.insert(cleaned)
                let tld = extractTLD(from: cleaned)
                domains.append((index + 1, cleaned, tld))
            }
        }

        // Same batch insert logic...
        let totalDomains = domains.count
        var importedCount = 0

        for batchStart in stride(from: 0, to: domains.count, by: batchSize) {
            let batchEnd = min(batchStart + batchSize, domains.count)
            let batch = Array(domains[batchStart..<batchEnd])

            try await context.perform {
                for (lineNumber, domain, tld) in batch {
                    let entity = DomainEntity(context: context)
                    entity.id = UUID()
                    entity.domain = domain
                    entity.status = DomainStatus.pending.rawValue
                    entity.importedAt = Date()
                    entity.lineNumber = Int32(lineNumber)
                    entity.tld = tld
                    entity.retryCount = 0
                    entity.session = session
                }

                try context.save()
            }

            importedCount += batch.count

            let progress = Double(importedCount) / Double(totalDomains)
            await MainActor.run {
                onProgress(progress)
            }
        }

        return importedCount
    }
}


     1→import Foundation
     2→import CoreData
     3→
     4→/// Service for exporting validation results
     5→actor ExportService {
     6→    static let shared = ExportService()
     7→
     8→    // MARK: - Export Formats
     9→
    10→    enum ExportFormat: String, CaseIterable {
    11→        case json = "JSON"
    12→        case csv = "CSV"
    13→
    14→        var fileExtension: String {
    15→            switch self {
    16→            case .json: return "json"
    17→            case .csv: return "csv"
    18→            }
    19→        }
    20→
    21→        var contentType: String {
    22→            switch self {
    23→            case .json: return "application/json"
    24→            case .csv: return "text/csv"
    25→            }
    26→        }
    27→    }
    28→
    29→    // MARK: - Export Options
    30→
    31→    struct ExportOptions {
    32→        var format: ExportFormat = .json
    33→        var statusFilter: Set<DomainStatus>? = nil
    34→        var includeResponseTime: Bool = true
    35→        var includeTimestamp: Bool = true
    36→        var includeDNSStatus: Bool = true
    37→        var includeHTTPCode: Bool = true
    38→
    39→        static var all: ExportOptions { ExportOptions() }
    40→        static var onlineOnly: ExportOptions { ExportOptions(statusFilter: [.online]) }
    41→        static var offlineOnly: ExportOptions { ExportOptions(statusFilter: [.offline, .dnsFailure, .timeout, .error]) }
    42→    }
    43→
    44→    // MARK: - Export to JSON
    45→
    46→    func exportToJSON(
    47→        session: ValidationSessionEntity,
    48→        domains: [DomainEntity],
    49→        options: ExportOptions,
    50→        onProgress: @escaping (Double) -> Void
    51→    ) async throws -> URL {
    52→        let total = domains.count
    53→        var exportedDomains: [[String: Any]] = []
    54→        exportedDomains.reserveCapacity(total)
    55→
    56→        // Filter domains
    57→        let filteredDomains: [DomainEntity]
    58→        if let statusFilter = options.statusFilter {
    59→            filteredDomains = domains.filter { domain in
    60→                guard let status = DomainStatus(rawValue: domain.status) else { return false }
    61→                return statusFilter.contains(status)
    62→            }
    63→        } else {
    64→            filteredDomains = domains
    65→        }
    66→
    67→        // Build domain records
    68→        for (index, domain) in filteredDomains.enumerated() {
    69→            var record: [String: Any] = [
    70→                "domain": domain.domain,
    71→                "status": domain.status
    72→            ]
    73→
    74→            if options.includeResponseTime && domain.responseTimeMs > 0 {
    75→                record["responseTimeMs"] = domain.responseTimeMs
    76→            }
    77→
    78→            if options.includeHTTPCode && domain.httpStatusCode > 0 {
    79→                record["httpStatusCode"] = domain.httpStatusCode
    80→            }
    81→
    82→            if options.includeDNSStatus {
    83→                record["dnsResolved"] = domain.dnsResolved
    84→            }
    85→
    86→            if options.includeTimestamp, let checkedAt = domain.lastCheckedAt {
    87→                record["checkedAt"] = ISO8601DateFormatter().string(from: checkedAt)
    88→            }
    89→
    90→            if let tld = domain.tld {
    91→                record["tld"] = tld
    92→            }
    93→
    94→            if let errorType = domain.errorType {
    95→                record["errorType"] = errorType
    96→            }
    97→
    98→            exportedDomains.append(record)
    99→
   100→            // Progress update every 1000 items
   101→            if index % 1000 == 0 {
   102→                await MainActor.run {
   103→                    onProgress(Double(index) / Double(filteredDomains.count))
   104→                }
   105→            }
   106→        }
   107→
   108→        // Build summary
   109→        let summary = calculateSummary(domains: filteredDomains)
   110→
   111→        // Build export object
   112→        let exportData: [String: Any] = [
   113→            "exportedAt": ISO8601DateFormatter().string(from: Date()),
   114→            "sessionName": session.name,
   115→            "sourceFile": session.sourceFileName,
   116→            "summary": [
   117→                "total": filteredDomains.count,
   118→                "online": summary.online,
   119→                "offline": summary.offline,
   120→                "dnsFailure": summary.dnsFailure,
   121→                "timeout": summary.timeout,
   122→                "error": summary.error,
   123→                "averageResponseTimeMs": summary.avgResponseTime
   124→            ],
   125→            "domains": exportedDomains
   126→        ]
   127→
   128→        // Write to temp file
   129→        let jsonData = try JSONSerialization.data(withJSONObject: exportData, options: [.prettyPrinted, .sortedKeys])
   130→        let filename = "domains_\(sanitizeFilename(session.name))_\(timestamp()).json"
   131→        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
   132→
   133→        try jsonData.write(to: tempURL)
   134→
   135→        await MainActor.run {
   136→            onProgress(1.0)
   137→        }
   138→
   139→        return tempURL
   140→    }
   141→
   142→    // MARK: - Export to CSV
   143→
   144→    func exportToCSV(
   145→        session: ValidationSessionEntity,
   146→        domains: [DomainEntity],
   147→        options: ExportOptions,
   148→        onProgress: @escaping (Double) -> Void
   149→    ) async throws -> URL {
   150→        // Filter domains
   151→        let filteredDomains: [DomainEntity]
   152→        if let statusFilter = options.statusFilter {
   153→            filteredDomains = domains.filter { domain in
   154→                guard let status = DomainStatus(rawValue: domain.status) else { return false }
   155→                return statusFilter.contains(status)
   156→            }
   157→        } else {
   158→            filteredDomains = domains
   159→        }
   160→
   161→        // Build header
   162→        var headers = ["domain", "status"]
   163→        if options.includeResponseTime { headers.append("response_time_ms") }
   164→        if options.includeHTTPCode { headers.append("http_status_code") }
   165→        if options.includeDNSStatus { headers.append("dns_resolved") }
   166→        if options.includeTimestamp { headers.append("checked_at") }
   167→        headers.append("tld")
   168→        headers.append("error_type")
   169→
   170→        var csv = headers.joined(separator: ",") + "\n"
   171→
   172→        // Build rows
   173→        let total = filteredDomains.count
   174→        for (index, domain) in filteredDomains.enumerated() {
   175→            var row = [
   176→                escapeCSV(domain.domain),
   177→                escapeCSV(domain.status)
   178→            ]
   179→
   180→            if options.includeResponseTime {
   181→                row.append(domain.responseTimeMs > 0 ? "\(domain.responseTimeMs)" : "")
   182→            }
   183→
   184→            if options.includeHTTPCode {
   185→                row.append(domain.httpStatusCode > 0 ? "\(domain.httpStatusCode)" : "")
   186→            }
   187→
   188→            if options.includeDNSStatus {
   189→                row.append(domain.dnsResolved ? "true" : "false")
   190→            }
   191→
   192→            if options.includeTimestamp {
   193→                if let date = domain.lastCheckedAt {
   194→                    row.append(ISO8601DateFormatter().string(from: date))
   195→                } else {
   196→                    row.append("")
   197→                }
   198→            }
   199→
   200→            row.append(escapeCSV(domain.tld ?? ""))
   201→            row.append(escapeCSV(domain.errorType ?? ""))
   202→
   203→            csv += row.joined(separator: ",") + "\n"
   204→
   205→            // Progress update every 5000 items
   206→            if index % 5000 == 0 {
   207→                await MainActor.run {
   208→                    onProgress(Double(index) / Double(total))
   209→                }
   210→            }
   211→        }
   212→
   213→        // Write to temp file
   214→        let filename = "domains_\(sanitizeFilename(session.name))_\(timestamp()).csv"
   215→        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
   216→
   217→        try csv.write(to: tempURL, atomically: true, encoding: .utf8)
   218→
   219→        await MainActor.run {
   220→            onProgress(1.0)
   221→        }
   222→
   223→        return tempURL
   224→    }
   225→
   226→    // MARK: - Helpers
   227→
   228→    private struct Summary {
   229→        var online = 0
   230→        var offline = 0
   231→        var dnsFailure = 0
   232→        var timeout = 0
   233→        var error = 0
   234→        var totalResponseTime = 0
   235→        var responseTimeCount = 0
   236→
   237→        var avgResponseTime: Int {
   238→            responseTimeCount > 0 ? totalResponseTime / responseTimeCount : 0
   239→        }
   240→    }
   241→
   242→    private func calculateSummary(domains: [DomainEntity]) -> Summary {
   243→        var summary = Summary()
   244→
   245→        for domain in domains {
   246→            switch DomainStatus(rawValue: domain.status) {
   247→            case .online:
   248→                summary.online += 1
   249→                if domain.responseTimeMs > 0 {
   250→                    summary.totalResponseTime += Int(domain.responseTimeMs)
   251→                    summary.responseTimeCount += 1
   252→                }
   253→            case .offline:
   254→                summary.offline += 1
   255→            case .dnsFailure:
   256→                summary.dnsFailure += 1
   257→            case .timeout:
   258→                summary.timeout += 1
   259→            case .error, .pending, .none:
   260→                summary.error += 1
   261→            }
   262→        }
   263→
   264→        return summary
   265→    }
   266→
   267→    private func escapeCSV(_ value: String) -> String {
   268→        if value.contains(",") || value.contains("\"") || value.contains("\n") || value.contains("\r") {
   269→            return "\"\(value.replacingOccurrences(of: "\"", with: "\"\""))\""
   270→        }
   271→        return value
   272→    }
   273→
   274→    private func timestamp() -> String {
   275→        let formatter = DateFormatter()
   276→        formatter.dateFormat = "yyyy-MM-dd_HHmmss"
   277→        return formatter.string(from: Date())
   278→    }
   279→
   280→    private func sanitizeFilename(_ name: String) -> String {
   281→        let invalidChars = CharacterSet(charactersIn: "/\\:*?\"<>|")
   282→        return name.components(separatedBy: invalidChars).joined(separator: "_")
   283→    }
   284→}
   285→

<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
import CoreData

/// Service for exporting validation results
actor ExportService {
    static let shared = ExportService()

    // MARK: - Export Formats

    enum ExportFormat: String, CaseIterable {
        case json = "JSON"
        case csv = "CSV"

        var fileExtension: String {
            switch self {
            case .json: return "json"
            case .csv: return "csv"
            }
        }

        var contentType: String {
            switch self {
            case .json: return "application/json"
            case .csv: return "text/csv"
            }
        }
    }

    // MARK: - Export Options

    struct ExportOptions {
        var format: ExportFormat = .json
        var statusFilter: Set<DomainStatus>? = nil
        var includeResponseTime: Bool = true
        var includeTimestamp: Bool = true
        var includeDNSStatus: Bool = true
        var includeHTTPCode: Bool = true

        static var all: ExportOptions { ExportOptions() }
        static var onlineOnly: ExportOptions { ExportOptions(statusFilter: [.online]) }
        static var offlineOnly: ExportOptions { ExportOptions(statusFilter: [.offline, .dnsFailure, .timeout, .error]) }
    }

    // MARK: - Export to JSON

    func exportToJSON(
        session: ValidationSessionEntity,
        domains: [DomainEntity],
        options: ExportOptions,
        onProgress: @escaping (Double) -> Void
    ) async throws -> URL {
        let total = domains.count
        var exportedDomains: [[String: Any]] = []
        exportedDomains.reserveCapacity(total)

        // Filter domains
        let filteredDomains: [DomainEntity]
        if let statusFilter = options.statusFilter {
            filteredDomains = domains.filter { domain in
                guard let status = DomainStatus(rawValue: domain.status) else { return false }
                return statusFilter.contains(status)
            }
        } else {
            filteredDomains = domains
        }

        // Build domain records
        for (index, domain) in filteredDomains.enumerated() {
            var record: [String: Any] = [
                "domain": domain.domain,
                "status": domain.status
            ]

            if options.includeResponseTime && domain.responseTimeMs > 0 {
                record["responseTimeMs"] = domain.responseTimeMs
            }

            if options.includeHTTPCode && domain.httpStatusCode > 0 {
                record["httpStatusCode"] = domain.httpStatusCode
            }

            if options.includeDNSStatus {
                record["dnsResolved"] = domain.dnsResolved
            }

            if options.includeTimestamp, let checkedAt = domain.lastCheckedAt {
                record["checkedAt"] = ISO8601DateFormatter().string(from: checkedAt)
            }

            if let tld = domain.tld {
                record["tld"] = tld
            }

            if let errorType = domain.errorType {
                record["errorType"] = errorType
            }

            exportedDomains.append(record)

            // Progress update every 1000 items
            if index % 1000 == 0 {
                await MainActor.run {
                    onProgress(Double(index) / Double(filteredDomains.count))
                }
            }
        }

        // Build summary
        let summary = calculateSummary(domains: filteredDomains)

        // Build export object
        let exportData: [String: Any] = [
            "exportedAt": ISO8601DateFormatter().string(from: Date()),
            "sessionName": session.name,
            "sourceFile": session.sourceFileName,
            "summary": [
                "total": filteredDomains.count,
                "online": summary.online,
                "offline": summary.offline,
                "dnsFailure": summary.dnsFailure,
                "timeout": summary.timeout,
                "error": summary.error,
                "averageResponseTimeMs": summary.avgResponseTime
            ],
            "domains": exportedDomains
        ]

        // Write to temp file
        let jsonData = try JSONSerialization.data(withJSONObject: exportData, options: [.prettyPrinted, .sortedKeys])
        let filename = "domains_\(sanitizeFilename(session.name))_\(timestamp()).json"
        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)

        try jsonData.write(to: tempURL)

        await MainActor.run {
            onProgress(1.0)
        }

        return tempURL
    }

    // MARK: - Export to CSV

    func exportToCSV(
        session: ValidationSessionEntity,
        domains: [DomainEntity],
        options: ExportOptions,
        onProgress: @escaping (Double) -> Void
    ) async throws -> URL {
        // Filter domains
        let filteredDomains: [DomainEntity]
        if let statusFilter = options.statusFilter {
            filteredDomains = domains.filter { domain in
                guard let status = DomainStatus(rawValue: domain.status) else { return false }
                return statusFilter.contains(status)
            }
        } else {
            filteredDomains = domains
        }

        // Build header
        var headers = ["domain", "status"]
        if options.includeResponseTime { headers.append("response_time_ms") }
        if options.includeHTTPCode { headers.append("http_status_code") }
        if options.includeDNSStatus { headers.append("dns_resolved") }
        if options.includeTimestamp { headers.append("checked_at") }
        headers.append("tld")
        headers.append("error_type")

        var csv = headers.joined(separator: ",") + "\n"

        // Build rows
        let total = filteredDomains.count
        for (index, domain) in filteredDomains.enumerated() {
            var row = [
                escapeCSV(domain.domain),
                escapeCSV(domain.status)
            ]

            if options.includeResponseTime {
                row.append(domain.responseTimeMs > 0 ? "\(domain.responseTimeMs)" : "")
            }

            if options.includeHTTPCode {
                row.append(domain.httpStatusCode > 0 ? "\(domain.httpStatusCode)" : "")
            }

            if options.includeDNSStatus {
                row.append(domain.dnsResolved ? "true" : "false")
            }

            if options.includeTimestamp {
                if let date = domain.lastCheckedAt {
                    row.append(ISO8601DateFormatter().string(from: date))
                } else {
                    row.append("")
                }
            }

            row.append(escapeCSV(domain.tld ?? ""))
            row.append(escapeCSV(domain.errorType ?? ""))

            csv += row.joined(separator: ",") + "\n"

            // Progress update every 5000 items
            if index % 5000 == 0 {
                await MainActor.run {
                    onProgress(Double(index) / Double(total))
                }
            }
        }

        // Write to temp file
        let filename = "domains_\(sanitizeFilename(session.name))_\(timestamp()).csv"
        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename)

        try csv.write(to: tempURL, atomically: true, encoding: .utf8)

        await MainActor.run {
            onProgress(1.0)
        }

        return tempURL
    }

    // MARK: - Helpers

    private struct Summary {
        var online = 0
        var offline = 0
        var dnsFailure = 0
        var timeout = 0
        var error = 0
        var totalResponseTime = 0
        var responseTimeCount = 0

        var avgResponseTime: Int {
            responseTimeCount > 0 ? totalResponseTime / responseTimeCount : 0
        }
    }

    private func calculateSummary(domains: [DomainEntity]) -> Summary {
        var summary = Summary()

        for domain in domains {
            switch DomainStatus(rawValue: domain.status) {
            case .online:
                summary.online += 1
                if domain.responseTimeMs > 0 {
                    summary.totalResponseTime += Int(domain.responseTimeMs)
                    summary.responseTimeCount += 1
                }
            case .offline:
                summary.offline += 1
            case .dnsFailure:
                summary.dnsFailure += 1
            case .timeout:
                summary.timeout += 1
            case .error, .pending, .none:
                summary.error += 1
            }
        }

        return summary
    }

    private func escapeCSV(_ value: String) -> String {
        if value.contains(",") || value.contains("\"") || value.contains("\n") || value.contains("\r") {
            return "\"\(value.replacingOccurrences(of: "\"", with: "\"\""))\""
        }
        return value
    }

    private func timestamp() -> String {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd_HHmmss"
        return formatter.string(from: Date())
    }

    private func sanitizeFilename(_ name: String) -> String {
        let invalidChars = CharacterSet(charactersIn: "/\\:*?\"<>|")
        return name.components(separatedBy: invalidChars).joined(separator: "_")
    }
}


     1→import Foundation
     2→import Network
     3→
     4→/// Actor responsible for domain validation via DNS + HTTP HEAD
     5→actor DomainValidator {
     6→    static let shared = DomainValidator()
     7→
     8→    // MARK: - Configuration
     9→
    10→    struct Config {
    11→        var concurrency: Int = 100
    12→        var requestTimeout: TimeInterval = 5
    13→        var resourceTimeout: TimeInterval = 10
    14→        var maxRetries: Int = 2
    15→        var dnsTimeout: TimeInterval = 3
    16→    }
    17→
    18→    private var config = Config()
    19→
    20→    // MARK: - State
    21→
    22→    private var isRunning = false
    23→    private var isPaused = false
    24→    private var shouldStop = false
    25→    private var currentTask: Task<Void, Never>?
    26→
    27→    // MARK: - Statistics
    28→
    29→    private var processedCount = 0
    30→    private var onlineCount = 0
    31→    private var offlineCount = 0
    32→    private var dnsFailureCount = 0
    33→    private var timeoutCount = 0
    34→    private var errorCount = 0
    35→    private var startTime: Date?
    36→    private var totalResponseTime: Int = 0
    37→
    38→    // MARK: - Caches
    39→
    40→    private var failedDomainCache: Set<String> = []
    41→    private let failedCacheTTL: TimeInterval = 300 // 5 minutes
    42→
    43→    // MARK: - URLSession
    44→
    45→    private lazy var session: URLSession = {
    46→        let config = URLSessionConfiguration.default
    47→        config.httpMaximumConnectionsPerHost = 80
    48→        config.timeoutIntervalForRequest = self.config.requestTimeout
    49→        config.timeoutIntervalForResource = self.config.resourceTimeout
    50→        config.waitsForConnectivity = false
    51→        config.urlCache = nil
    52→        config.requestCachePolicy = .reloadIgnoringLocalCacheData
    53→        return URLSession(configuration: config)
    54→    }()
    55→
    56→    // MARK: - Public Interface
    57→
    58→    var isValidating: Bool {
    59→        isRunning && !isPaused
    60→    }
    61→
    62→    var isPausedState: Bool {
    63→        isPaused
    64→    }
    65→
    66→    /// Start validation for domains
    67→    func startValidation(
    68→        domains: [DomainEntity],
    69→        onProgress: @escaping (ValidationProgress) -> Void,
    70→        onResult: @escaping (DomainEntity, DomainResult) -> Void
    71→    ) async {
    72→        guard !isRunning else { return }
    73→
    74→        isRunning = true
    75→        isPaused = false
    76→        shouldStop = false
    77→        startTime = Date()
    78→
    79→        // Reset stats
    80→        processedCount = 0
    81→        onlineCount = 0
    82→        offlineCount = 0
    83→        dnsFailureCount = 0
    84→        timeoutCount = 0
    85→        errorCount = 0
    86→        totalResponseTime = 0
    87→
    88→        let total = domains.count
    89→
    90→        // Sliding window concurrent validation
    91→        await withTaskGroup(of: (DomainEntity, DomainResult).self) { group in
    92→            var domainIndex = 0
    93→
    94→            // Fill initial window
    95→            while domainIndex < min(config.concurrency, domains.count) && !shouldStop {
    96→                let domain = domains[domainIndex]
    97→                group.addTask {
    98→                    let result = await self.validateDomain(domain.domain)
    99→                    return (domain, result)
   100→                }
   101→                domainIndex += 1
   102→            }
   103→
   104→            // Process results and add new tasks
   105→            for await (entity, result) in group {
   106→                if shouldStop { break }
   107→
   108→                // Wait if paused
   109→                while isPaused && !shouldStop {
   110→                    try? await Task.sleep(nanoseconds: 100_000_000) // 100ms
   111→                }
   112→
   113→                if shouldStop { break }
   114→
   115→                // Update stats
   116→                processedCount += 1
   117→                updateStats(result)
   118→
   119→                // Callback for result
   120→                await MainActor.run {
   121→                    onResult(entity, result)
   122→                }
   123→
   124→                // Add next domain to maintain concurrency
   125→                if domainIndex < domains.count && !shouldStop {
   126→                    let domain = domains[domainIndex]
   127→                    group.addTask {
   128→                        let result = await self.validateDomain(domain.domain)
   129→                        return (domain, result)
   130→                    }
   131→                    domainIndex += 1
   132→                }
   133→
   134→                // Report progress every 10 domains or at completion
   135→                if processedCount % 10 == 0 || processedCount == total {
   136→                    let progress = calculateProgress(total: total)
   137→                    await MainActor.run {
   138→                        onProgress(progress)
   139→                    }
   140→                }
   141→            }
   142→        }
   143→
   144→        isRunning = false
   145→        isPaused = false
   146→
   147→        // Final progress update
   148→        let finalProgress = calculateProgress(total: total)
   149→        await MainActor.run {
   150→            onProgress(finalProgress)
   151→        }
   152→    }
   153→
   154→    func pause() {
   155→        isPaused = true
   156→    }
   157→
   158→    func resume() {
   159→        isPaused = false
   160→    }
   161→
   162→    func stop() {
   163→        shouldStop = true
   164→        isPaused = false
   165→        currentTask?.cancel()
   166→    }
   167→
   168→    func updateConfig(_ newConfig: Config) {
   169→        config = newConfig
   170→    }
   171→
   172→    // MARK: - Validation Logic
   173→
   174→    /// Validate a single domain with DNS + HTTP HEAD
   175→    private func validateDomain(_ domain: String) async -> DomainResult {
   176→        let startTime = CFAbsoluteTimeGetCurrent()
   177→
   178→        // Check if in failed cache
   179→        if failedDomainCache.contains(domain) {
   180→            return DomainResult(
   181→                domain: domain,
   182→                status: .offline,
   183→                dnsResolved: false,
   184→                errorType: "cached_failure",
   185→                errorMessage: "Previously failed"
   186→            )
   187→        }
   188→
   189→        // STEP 1: DNS Resolution
   190→        let dnsResult = await resolveDNS(domain)
   191→
   192→        guard dnsResult.resolved else {
   193→            failedDomainCache.insert(domain)
   194→            return DomainResult(
   195→                domain: domain,
   196→                status: .dnsFailure,
   197→                dnsResolved: false,
   198→                errorType: "dns_failure",
   199→                errorMessage: dnsResult.errorMessage ?? "DNS resolution failed"
   200→            )
   201→        }
   202→
   203→        // STEP 2: HTTP HEAD Request
   204→        let httpResult = await performHTTPCheck(domain, startTime: startTime)
   205→
   206→        if httpResult.status.isFailed {
   207→            failedDomainCache.insert(domain)
   208→        }
   209→
   210→        return httpResult
   211→    }
   212→
   213→    /// Resolve DNS for domain
   214→    private func resolveDNS(_ domain: String) async -> DNSResult {
   215→        await withCheckedContinuation { continuation in
   216→            let host = CFHostCreateWithName(nil, domain as CFString).takeRetainedValue()
   217→
   218→            // Set up resolution with timeout
   219→            let queue = DispatchQueue.global(qos: .utility)
   220→            var resolved = false
   221→            var timedOut = false
   222→
   223→            // Timeout handler
   224→            queue.asyncAfter(deadline: .now() + config.dnsTimeout) {
   225→                if !resolved {
   226→                    timedOut = true
   227→                    continuation.resume(returning: DNSResult(
   228→                        resolved: false,
   229→                        errorMessage: "DNS timeout"
   230→                    ))
   231→                }
   232→            }
   233→
   234→            // Perform resolution
   235→            queue.async {
   236→                var error = CFStreamError()
   237→                let success = CFHostStartInfoResolution(host, .addresses, &error)
   238→
   239→                guard !timedOut else { return }
   240→                resolved = true
   241→
   242→                if success {
   243→                    var hasBeenResolved = DarwinBoolean(false)
   244→                    if let addresses = CFHostGetAddressing(host, &hasBeenResolved)?.takeUnretainedValue() as? [Data],
   245→                       hasBeenResolved.boolValue,
   246→                       !addresses.isEmpty {
   247→                        continuation.resume(returning: DNSResult(resolved: true, ipAddress: "resolved"))
   248→                    } else {
   249→                        continuation.resume(returning: DNSResult(resolved: false, errorMessage: "No addresses found"))
   250→                    }
   251→                } else {
   252→                    continuation.resume(returning: DNSResult(resolved: false, errorMessage: "Resolution failed"))
   253→                }
   254→            }
   255→        }
   256→    }
   257→
   258→    /// Perform HTTP HEAD check
   259→    private func performHTTPCheck(_ domain: String, startTime: CFAbsoluteTime) async -> DomainResult {
   260→        // Try HTTPS first, then HTTP
   261→        for scheme in ["https", "http"] {
   262→            guard let url = URL(string: "\(scheme)://\(domain)") else { continue }
   263→
   264→            var request = URLRequest(url: url)
   265→            request.httpMethod = "HEAD"
   266→            request.cachePolicy = .reloadIgnoringLocalCacheData
   267→            request.timeoutInterval = config.requestTimeout
   268→
   269→            // Add user agent
   270→            request.setValue("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) DomainChecker/1.0", forHTTPHeaderField: "User-Agent")
   271→
   272→            do {
   273→                let (_, response) = try await session.data(for: request)
   274→                let elapsed = Int((CFAbsoluteTimeGetCurrent() - startTime) * 1000)
   275→
   276→                if let httpResponse = response as? HTTPURLResponse {
   277→                    // Consider 1xx-4xx as "online" (server responded)
   278→                    if (100...499).contains(httpResponse.statusCode) {
   279→                        return DomainResult(
   280→                            domain: domain,
   281→                            status: .online,
   282→                            responseTimeMs: elapsed,
   283→                            httpStatusCode: httpResponse.statusCode,
   284→                            dnsResolved: true
   285→                        )
   286→                    } else {
   287→                        // 5xx errors - server error but domain exists
   288→                        return DomainResult(
   289→                            domain: domain,
   290→                            status: .offline,
   291→                            responseTimeMs: elapsed,
   292→                            httpStatusCode: httpResponse.statusCode,
   293→                            dnsResolved: true,
   294→                            errorType: "server_error",
   295→                            errorMessage: "HTTP \(httpResponse.statusCode)"
   296→                        )
   297→                    }
   298→                }
   299→            } catch let error as URLError {
   300→                // Handle specific URL errors
   301→                switch error.code {
   302→                case .timedOut:
   303→                    // Only return timeout on last scheme attempt
   304→                    if scheme == "http" {
   305→                        return DomainResult(
   306→                            domain: domain,
   307→                            status: .timeout,
   308→                            dnsResolved: true,
   309→                            errorType: "timeout",
   310→                            errorMessage: "Connection timed out"
   311→                        )
   312→                    }
   313→
   314→                case .cannotConnectToHost, .networkConnectionLost:
   315→                    // Try next scheme
   316→                    continue
   317→
   318→                case .secureConnectionFailed, .serverCertificateUntrusted:
   319→                    // SSL error on HTTPS, try HTTP
   320→                    if scheme == "https" { continue }
   321→                    return DomainResult(
   322→                        domain: domain,
   323→                        status: .offline,
   324→                        dnsResolved: true,
   325→                        errorType: "ssl_error",
   326→                        errorMessage: error.localizedDescription
   327→                    )
   328→
   329→                default:
   330→                    // Try next scheme
   331→                    if scheme == "https" { continue }
   332→                }
   333→            } catch {
   334→                // Try next scheme
   335→                if scheme == "https" { continue }
   336→            }
   337→        }
   338→
   339→        // Both schemes failed
   340→        return DomainResult(
   341→            domain: domain,
   342→            status: .offline,
   343→            dnsResolved: true,
   344→            errorType: "connection_failed",
   345→            errorMessage: "Could not connect via HTTPS or HTTP"
   346→        )
   347→    }
   348→
   349→    // MARK: - Statistics
   350→
   351→    private func updateStats(_ result: DomainResult) {
   352→        switch result.status {
   353→        case .online:
   354→            onlineCount += 1
   355→            if let responseTime = result.responseTimeMs {
   356→                totalResponseTime += responseTime
   357→            }
   358→        case .offline:
   359→            offlineCount += 1
   360→        case .dnsFailure:
   361→            dnsFailureCount += 1
   362→        case .timeout:
   363→            timeoutCount += 1
   364→        case .error, .pending:
   365→            errorCount += 1
   366→        }
   367→    }
   368→
   369→    private func calculateProgress(total: Int) -> ValidationProgress {
   370→        let elapsed = Date().timeIntervalSince(startTime ?? Date())
   371→        let domainsPerSecond = elapsed > 0 ? Double(processedCount) / elapsed : 0
   372→
   373→        let remaining = total - processedCount
   374→        let estimatedSeconds = domainsPerSecond > 0 ? Int(Double(remaining) / domainsPerSecond) : 0
   375→
   376→        return ValidationProgress(
   377→            processed: processedCount,
   378→            total: total,
   379→            onlineCount: onlineCount,
   380→            offlineCount: offlineCount,
   381→            errorCount: dnsFailureCount + timeoutCount + errorCount,
   382→            domainsPerSecond: domainsPerSecond,
   383→            estimatedSecondsRemaining: estimatedSeconds
   384→        )
   385→    }
   386→
   387→    // MARK: - Cache Management
   388→
   389→    func clearFailedCache() {
   390→        failedDomainCache.removeAll()
   391→    }
   392→
   393→    func removeFromFailedCache(_ domain: String) {
   394→        failedDomainCache.remove(domain)
   395→    }
   396→}
   397→
   398→// MARK: - Validation with Retry
   399→
   400→extension DomainValidator {
   401→    /// Validate with retry logic
   402→    private func validateWithRetry(_ domain: String, maxRetries: Int) async -> DomainResult {
   403→        var lastResult: DomainResult?
   404→
   405→        for attempt in 0...maxRetries {
   406→            let result = await validateDomain(domain)
   407→
   408→            // Success - return immediately
   409→            if result.status == .online {
   410→                return result
   411→            }
   412→
   413→            // DNS failure - don't retry (domain doesn't exist)
   414→            if result.status == .dnsFailure {
   415→                return result
   416→            }
   417→
   418→            lastResult = result
   419→
   420→            // Exponential backoff before retry
   421→            if attempt < maxRetries {
   422→                let delay = pow(2.0, Double(attempt)) * 0.5 // 0.5s, 1s, 2s...
   423→                let jitter = Double.random(in: 0...0.3) * delay
   424→                try? await Task.sleep(nanoseconds: UInt64((delay + jitter) * 1_000_000_000))
   425→            }
   426→        }
   427→
   428→        return lastResult ?? DomainResult(domain: domain, status: .error, errorType: "unknown", errorMessage: "All retries failed")
   429→    }
   430→}
   431→

<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
import Network

/// Actor responsible for domain validation via DNS + HTTP HEAD
actor DomainValidator {
    static let shared = DomainValidator()

    // MARK: - Configuration

    struct Config {
        var concurrency: Int = 100
        var requestTimeout: TimeInterval = 5
        var resourceTimeout: TimeInterval = 10
        var maxRetries: Int = 2
        var dnsTimeout: TimeInterval = 3
    }

    private var config = Config()

    // MARK: - State

    private var isRunning = false
    private var isPaused = false
    private var shouldStop = false
    private var currentTask: Task<Void, Never>?

    // MARK: - Statistics

    private var processedCount = 0
    private var onlineCount = 0
    private var offlineCount = 0
    private var dnsFailureCount = 0
    private var timeoutCount = 0
    private var errorCount = 0
    private var startTime: Date?
    private var totalResponseTime: Int = 0

    // MARK: - Caches

    private var failedDomainCache: Set<String> = []
    private let failedCacheTTL: TimeInterval = 300 // 5 minutes

    // MARK: - URLSession

    private lazy var session: URLSession = {
        let config = URLSessionConfiguration.default
        config.httpMaximumConnectionsPerHost = 80
        config.timeoutIntervalForRequest = self.config.requestTimeout
        config.timeoutIntervalForResource = self.config.resourceTimeout
        config.waitsForConnectivity = false
        config.urlCache = nil
        config.requestCachePolicy = .reloadIgnoringLocalCacheData
        return URLSession(configuration: config)
    }()

    // MARK: - Public Interface

    var isValidating: Bool {
        isRunning && !isPaused
    }

    var isPausedState: Bool {
        isPaused
    }

    /// Start validation for domains
    func startValidation(
        domains: [DomainEntity],
        onProgress: @escaping (ValidationProgress) -> Void,
        onResult: @escaping (DomainEntity, DomainResult) -> Void
    ) async {
        guard !isRunning else { return }

        isRunning = true
        isPaused = false
        shouldStop = false
        startTime = Date()

        // Reset stats
        processedCount = 0
        onlineCount = 0
        offlineCount = 0
        dnsFailureCount = 0
        timeoutCount = 0
        errorCount = 0
        totalResponseTime = 0

        let total = domains.count

        // Sliding window concurrent validation
        await withTaskGroup(of: (DomainEntity, DomainResult).self) { group in
            var domainIndex = 0

            // Fill initial window
            while domainIndex < min(config.concurrency, domains.count) && !shouldStop {
                let domain = domains[domainIndex]
                group.addTask {
                    let result = await self.validateDomain(domain.domain)
                    return (domain, result)
                }
                domainIndex += 1
            }

            // Process results and add new tasks
            for await (entity, result) in group {
                if shouldStop { break }

                // Wait if paused
                while isPaused && !shouldStop {
                    try? await Task.sleep(nanoseconds: 100_000_000) // 100ms
                }

                if shouldStop { break }

                // Update stats
                processedCount += 1
                updateStats(result)

                // Callback for result
                await MainActor.run {
                    onResult(entity, result)
                }

                // Add next domain to maintain concurrency
                if domainIndex < domains.count && !shouldStop {
                    let domain = domains[domainIndex]
                    group.addTask {
                        let result = await self.validateDomain(domain.domain)
                        return (domain, result)
                    }
                    domainIndex += 1
                }

                // Report progress every 10 domains or at completion
                if processedCount % 10 == 0 || processedCount == total {
                    let progress = calculateProgress(total: total)
                    await MainActor.run {
                        onProgress(progress)
                    }
                }
            }
        }

        isRunning = false
        isPaused = false

        // Final progress update
        let finalProgress = calculateProgress(total: total)
        await MainActor.run {
            onProgress(finalProgress)
        }
    }

    func pause() {
        isPaused = true
    }

    func resume() {
        isPaused = false
    }

    func stop() {
        shouldStop = true
        isPaused = false
        currentTask?.cancel()
    }

    func updateConfig(_ newConfig: Config) {
        config = newConfig
    }

    // MARK: - Validation Logic

    /// Validate a single domain with DNS + HTTP HEAD
    private func validateDomain(_ domain: String) async -> DomainResult {
        let startTime = CFAbsoluteTimeGetCurrent()

        // Check if in failed cache
        if failedDomainCache.contains(domain) {
            return DomainResult(
                domain: domain,
                status: .offline,
                dnsResolved: false,
                errorType: "cached_failure",
                errorMessage: "Previously failed"
            )
        }

        // STEP 1: DNS Resolution
        let dnsResult = await resolveDNS(domain)

        guard dnsResult.resolved else {
            failedDomainCache.insert(domain)
            return DomainResult(
                domain: domain,
                status: .dnsFailure,
                dnsResolved: false,
                errorType: "dns_failure",
                errorMessage: dnsResult.errorMessage ?? "DNS resolution failed"
            )
        }

        // STEP 2: HTTP HEAD Request
        let httpResult = await performHTTPCheck(domain, startTime: startTime)

        if httpResult.status.isFailed {
            failedDomainCache.insert(domain)
        }

        return httpResult
    }

    /// Resolve DNS for domain
    private func resolveDNS(_ domain: String) async -> DNSResult {
        await withCheckedContinuation { continuation in
            let host = CFHostCreateWithName(nil, domain as CFString).takeRetainedValue()

            // Set up resolution with timeout
            let queue = DispatchQueue.global(qos: .utility)
            var resolved = false
            var timedOut = false

            // Timeout handler
            queue.asyncAfter(deadline: .now() + config.dnsTimeout) {
                if !resolved {
                    timedOut = true
                    continuation.resume(returning: DNSResult(
                        resolved: false,
                        errorMessage: "DNS timeout"
                    ))
                }
            }

            // Perform resolution
            queue.async {
                var error = CFStreamError()
                let success = CFHostStartInfoResolution(host, .addresses, &error)

                guard !timedOut else { return }
                resolved = true

                if success {
                    var hasBeenResolved = DarwinBoolean(false)
                    if let addresses = CFHostGetAddressing(host, &hasBeenResolved)?.takeUnretainedValue() as? [Data],
                       hasBeenResolved.boolValue,
                       !addresses.isEmpty {
                        continuation.resume(returning: DNSResult(resolved: true, ipAddress: "resolved"))
                    } else {
                        continuation.resume(returning: DNSResult(resolved: false, errorMessage: "No addresses found"))
                    }
                } else {
                    continuation.resume(returning: DNSResult(resolved: false, errorMessage: "Resolution failed"))
                }
            }
        }
    }

    /// Perform HTTP HEAD check
    private func performHTTPCheck(_ domain: String, startTime: CFAbsoluteTime) async -> DomainResult {
        // Try HTTPS first, then HTTP
        for scheme in ["https", "http"] {
            guard let url = URL(string: "\(scheme)://\(domain)") else { continue }

            var request = URLRequest(url: url)
            request.httpMethod = "HEAD"
            request.cachePolicy = .reloadIgnoringLocalCacheData
            request.timeoutInterval = config.requestTimeout

            // Add user agent
            request.setValue("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) DomainChecker/1.0", forHTTPHeaderField: "User-Agent")

            do {
                let (_, response) = try await session.data(for: request)
                let elapsed = Int((CFAbsoluteTimeGetCurrent() - startTime) * 1000)

                if let httpResponse = response as? HTTPURLResponse {
                    // Consider 1xx-4xx as "online" (server responded)
                    if (100...499).contains(httpResponse.statusCode) {
                        return DomainResult(
                            domain: domain,
                            status: .online,
                            responseTimeMs: elapsed,
                            httpStatusCode: httpResponse.statusCode,
                            dnsResolved: true
                        )
                    } else {
                        // 5xx errors - server error but domain exists
                        return DomainResult(
                            domain: domain,
                            status: .offline,
                            responseTimeMs: elapsed,
                            httpStatusCode: httpResponse.statusCode,
                            dnsResolved: true,
                            errorType: "server_error",
                            errorMessage: "HTTP \(httpResponse.statusCode)"
                        )
                    }
                }
            } catch let error as URLError {
                // Handle specific URL errors
                switch error.code {
                case .timedOut:
                    // Only return timeout on last scheme attempt
                    if scheme == "http" {
                        return DomainResult(
                            domain: domain,
                            status: .timeout,
                            dnsResolved: true,
                            errorType: "timeout",
                            errorMessage: "Connection timed out"
                        )
                    }

                case .cannotConnectToHost, .networkConnectionLost:
                    // Try next scheme
                    continue

                case .secureConnectionFailed, .serverCertificateUntrusted:
                    // SSL error on HTTPS, try HTTP
                    if scheme == "https" { continue }
                    return DomainResult(
                        domain: domain,
                        status: .offline,
                        dnsResolved: true,
                        errorType: "ssl_error",
                        errorMessage: error.localizedDescription
                    )

                default:
                    // Try next scheme
                    if scheme == "https" { continue }
                }
            } catch {
                // Try next scheme
                if scheme == "https" { continue }
            }
        }

        // Both schemes failed
        return DomainResult(
            domain: domain,
            status: .offline,
            dnsResolved: true,
            errorType: "connection_failed",
            errorMessage: "Could not connect via HTTPS or HTTP"
        )
    }

    // MARK: - Statistics

    private func updateStats(_ result: DomainResult) {
        switch result.status {
        case .online:
            onlineCount += 1
            if let responseTime = result.responseTimeMs {
                totalResponseTime += responseTime
            }
        case .offline:
            offlineCount += 1
        case .dnsFailure:
            dnsFailureCount += 1
        case .timeout:
            timeoutCount += 1
        case .error, .pending:
            errorCount += 1
        }
    }

    private func calculateProgress(total: Int) -> ValidationProgress {
        let elapsed = Date().timeIntervalSince(startTime ?? Date())
        let domainsPerSecond = elapsed > 0 ? Double(processedCount) / elapsed : 0

        let remaining = total - processedCount
        let estimatedSeconds = domainsPerSecond > 0 ? Int(Double(remaining) / domainsPerSecond) : 0

        return ValidationProgress(
            processed: processedCount,
            total: total,
            onlineCount: onlineCount,
            offlineCount: offlineCount,
            errorCount: dnsFailureCount + timeoutCount + errorCount,
            domainsPerSecond: domainsPerSecond,
            estimatedSecondsRemaining: estimatedSeconds
        )
    }

    // MARK: - Cache Management

    func clearFailedCache() {
        failedDomainCache.removeAll()
    }

    func removeFromFailedCache(_ domain: String) {
        failedDomainCache.remove(domain)
    }
}

// MARK: - Validation with Retry

extension DomainValidator {
    /// Validate with retry logic
    private func validateWithRetry(_ domain: String, maxRetries: Int) async -> DomainResult {
        var lastResult: DomainResult?

        for attempt in 0...maxRetries {
            let result = await validateDomain(domain)

            // Success - return immediately
            if result.status == .online {
                return result
            }

            // DNS failure - don't retry (domain doesn't exist)
            if result.status == .dnsFailure {
                return result
            }

            lastResult = result

            // Exponential backoff before retry
            if attempt < maxRetries {
                let delay = pow(2.0, Double(attempt)) * 0.5 // 0.5s, 1s, 2s...
                let jitter = Double.random(in: 0...0.3) * delay
                try? await Task.sleep(nanoseconds: UInt64((delay + jitter) * 1_000_000_000))
            }
        }

        return lastResult ?? DomainResult(domain: domain, status: .error, errorType: "unknown", errorMessage: "All retries failed")
    }
}


     1→import Foundation
     2→import SwiftUI
     3→
     4→/// Status of domain validation
     5→enum DomainStatus: String, CaseIterable, Codable {
     6→    case pending = "pending"
     7→    case online = "online"
     8→    case offline = "offline"
     9→    case timeout = "timeout"
    10→    case dnsFailure = "dns_failure"
    11→    case error = "error"
    12→
    13→    var displayName: String {
    14→        switch self {
    15→        case .pending: return "Pending"
    16→        case .online: return "Online"
    17→        case .offline: return "Offline"
    18→        case .timeout: return "Timeout"
    19→        case .dnsFailure: return "DNS Failed"
    20→        case .error: return "Error"
    21→        }
    22→    }
    23→
    24→    var color: Color {
    25→        switch self {
    26→        case .pending: return .orange
    27→        case .online: return .green
    28→        case .offline: return .red
    29→        case .timeout: return .purple
    30→        case .dnsFailure: return .pink
    31→        case .error: return .gray
    32→        }
    33→    }
    34→
    35→    var icon: String {
    36→        switch self {
    37→        case .pending: return "clock.fill"
    38→        case .online: return "checkmark.circle.fill"
    39→        case .offline: return "xmark.circle.fill"
    40→        case .timeout: return "clock.badge.xmark.fill"
    41→        case .dnsFailure: return "network.slash"
    42→        case .error: return "exclamationmark.triangle.fill"
    43→        }
    44→    }
    45→
    46→    /// Is this a "failed" status (not online)?
    47→    var isFailed: Bool {
    48→        self != .online && self != .pending
    49→    }
    50→}
    51→
    52→/// Result of a single domain validation
    53→struct DomainResult: Sendable {
    54→    let domain: String
    55→    let status: DomainStatus
    56→    let responseTimeMs: Int?
    57→    let httpStatusCode: Int?
    58→    let dnsResolved: Bool
    59→    let errorType: String?
    60→    let errorMessage: String?
    61→    let checkedAt: Date
    62→
    63→    init(
    64→        domain: String,
    65→        status: DomainStatus,
    66→        responseTimeMs: Int? = nil,
    67→        httpStatusCode: Int? = nil,
    68→        dnsResolved: Bool = false,
    69→        errorType: String? = nil,
    70→        errorMessage: String? = nil
    71→    ) {
    72→        self.domain = domain
    73→        self.status = status
    74→        self.responseTimeMs = responseTimeMs
    75→        self.httpStatusCode = httpStatusCode
    76→        self.dnsResolved = dnsResolved
    77→        self.errorType = errorType
    78→        self.errorMessage = errorMessage
    79→        self.checkedAt = Date()
    80→    }
    81→}
    82→
    83→/// DNS resolution result
    84→struct DNSResult: Sendable {
    85→    let resolved: Bool
    86→    let ipAddress: String?
    87→    let errorMessage: String?
    88→
    89→    init(resolved: Bool, ipAddress: String? = nil, errorMessage: String? = nil) {
    90→        self.resolved = resolved
    91→        self.ipAddress = ipAddress
    92→        self.errorMessage = errorMessage
    93→    }
    94→}
    95→
    96→/// Validation progress info
    97→struct ValidationProgress: Sendable {
    98→    let processed: Int
    99→    let total: Int
   100→    let onlineCount: Int
   101→    let offlineCount: Int
   102→    let errorCount: Int
   103→    let domainsPerSecond: Double
   104→    let estimatedSecondsRemaining: Int
   105→
   106→    var percentComplete: Double {
   107→        guard total > 0 else { return 0 }
   108→        return Double(processed) / Double(total)
   109→    }
   110→
   111→    var formattedETA: String {
   112→        if estimatedSecondsRemaining <= 0 { return "--" }
   113→
   114→        let hours = estimatedSecondsRemaining / 3600
   115→        let minutes = (estimatedSecondsRemaining % 3600) / 60
   116→        let seconds = estimatedSecondsRemaining % 60
   117→
   118→        if hours > 0 {
   119→            return String(format: "%dh %02dm", hours, minutes)
   120→        } else if minutes > 0 {
   121→            return String(format: "%dm %02ds", minutes, seconds)
   122→        } else {
   123→            return String(format: "%ds", seconds)
   124→        }
   125→    }
   126→}
   127→
   128→/// Session statistics
   129→struct SessionStats {
   130→    let totalDomains: Int
   131→    let processedDomains: Int
   132→    let onlineCount: Int
   133→    let offlineCount: Int
   134→    let dnsFailureCount: Int
   135→    let timeoutCount: Int
   136→    let errorCount: Int
   137→    let averageResponseTime: Double
   138→
   139→    var pendingCount: Int {
   140→        totalDomains - processedDomains
   141→    }
   142→
   143→    var successRate: Double {
   144→        guard processedDomains > 0 else { return 0 }
   145→        return Double(onlineCount) / Double(processedDomains)
   146→    }
   147→}
   148→

<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
import SwiftUI

/// Status of domain validation
enum DomainStatus: String, CaseIterable, Codable {
    case pending = "pending"
    case online = "online"
    case offline = "offline"
    case timeout = "timeout"
    case dnsFailure = "dns_failure"
    case error = "error"

    var displayName: String {
        switch self {
        case .pending: return "Pending"
        case .online: return "Online"
        case .offline: return "Offline"
        case .timeout: return "Timeout"
        case .dnsFailure: return "DNS Failed"
        case .error: return "Error"
        }
    }

    var color: Color {
        switch self {
        case .pending: return .orange
        case .online: return .green
        case .offline: return .red
        case .timeout: return .purple
        case .dnsFailure: return .pink
        case .error: return .gray
        }
    }

    var icon: String {
        switch self {
        case .pending: return "clock.fill"
        case .online: return "checkmark.circle.fill"
        case .offline: return "xmark.circle.fill"
        case .timeout: return "clock.badge.xmark.fill"
        case .dnsFailure: return "network.slash"
        case .error: return "exclamationmark.triangle.fill"
        }
    }

    /// Is this a "failed" status (not online)?
    var isFailed: Bool {
        self != .online && self != .pending
    }
}

/// Result of a single domain validation
struct DomainResult: Sendable {
    let domain: String
    let status: DomainStatus
    let responseTimeMs: Int?
    let httpStatusCode: Int?
    let dnsResolved: Bool
    let errorType: String?
    let errorMessage: String?
    let checkedAt: Date

    init(
        domain: String,
        status: DomainStatus,
        responseTimeMs: Int? = nil,
        httpStatusCode: Int? = nil,
        dnsResolved: Bool = false,
        errorType: String? = nil,
        errorMessage: String? = nil
    ) {
        self.domain = domain
        self.status = status
        self.responseTimeMs = responseTimeMs
        self.httpStatusCode = httpStatusCode
        self.dnsResolved = dnsResolved
        self.errorType = errorType
        self.errorMessage = errorMessage
        self.checkedAt = Date()
    }
}

/// DNS resolution result
struct DNSResult: Sendable {
    let resolved: Bool
    let ipAddress: String?
    let errorMessage: String?

    init(resolved: Bool, ipAddress: String? = nil, errorMessage: String? = nil) {
        self.resolved = resolved
        self.ipAddress = ipAddress
        self.errorMessage = errorMessage
    }
}

/// Validation progress info
struct ValidationProgress: Sendable {
    let processed: Int
    let total: Int
    let onlineCount: Int
    let offlineCount: Int
    let errorCount: Int
    let domainsPerSecond: Double
    let estimatedSecondsRemaining: Int

    var percentComplete: Double {
        guard total > 0 else { return 0 }
        return Double(processed) / Double(total)
    }

    var formattedETA: String {
        if estimatedSecondsRemaining <= 0 { return "--" }

        let hours = estimatedSecondsRemaining / 3600
        let minutes = (estimatedSecondsRemaining % 3600) / 60
        let seconds = estimatedSecondsRemaining % 60

        if hours > 0 {
            return String(format: "%dh %02dm", hours, minutes)
        } else if minutes > 0 {
            return String(format: "%dm %02ds", minutes, seconds)
        } else {
            return String(format: "%ds", seconds)
        }
    }
}

/// Session statistics
struct SessionStats {
    let totalDomains: Int
    let processedDomains: Int
    let onlineCount: Int
    let offlineCount: Int
    let dnsFailureCount: Int
    let timeoutCount: Int
    let errorCount: Int
    let averageResponseTime: Double

    var pendingCount: Int {
        totalDomains - processedDomains
    }

    var successRate: Double {
        guard processedDomains > 0 else { return 0 }
        return Double(onlineCount) / Double(processedDomains)
    }
}


     1→import SwiftUI
     2→import UniformTypeIdentifiers
     3→
     4→struct ImportView: View {
     5→    @Environment(\.dismiss) private var dismiss
     6→    @EnvironmentObject var dataManager: DataManager
     7→
     8→    @State private var selectedFileURL: URL?
     9→    @State private var sessionName = ""
    10→    @State private var isImporting = false
    11→    @State private var importProgress: Double = 0
    12→    @State private var importedCount = 0
    13→    @State private var errorMessage: String?
    14→    @State private var showingFilePicker = false
    15→
    16→    var body: some View {
    17→        VStack(spacing: 24) {
    18→            // Header
    19→            VStack(spacing: 8) {
    20→                Image(systemName: "square.and.arrow.down.fill")
    21→                    .font(.system(size: 48))
    22→                    .foregroundStyle(.blue)
    23→
    24→                Text("Import Domains")
    25→                    .font(.title.bold())
    26→
    27→                Text("Select a text file containing domain names (one per line)")
    28→                    .font(.subheadline)
    29→                    .foregroundStyle(.secondary)
    30→                    .multilineTextAlignment(.center)
    31→            }
    32→
    33→            // File Selection
    34→            VStack(alignment: .leading, spacing: 8) {
    35→                Text("Source File")
    36→                    .font(.headline)
    37→
    38→                HStack {
    39→                    if let url = selectedFileURL {
    40→                        Label(url.lastPathComponent, systemImage: "doc.text.fill")
    41→                            .foregroundStyle(.secondary)
    42→                    } else {
    43→                        Text("No file selected")
    44→                            .foregroundStyle(.secondary)
    45→                    }
    46→
    47→                    Spacer()
    48→
    49→                    Button("Choose File...") {
    50→                        showingFilePicker = true
    51→                    }
    52→                }
    53→                .padding()
    54→                .background(Color(nsColor: .textBackgroundColor))
    55→                .clipShape(RoundedRectangle(cornerRadius: 8))
    56→            }
    57→
    58→            // Session Name
    59→            VStack(alignment: .leading, spacing: 8) {
    60→                Text("Session Name (optional)")
    61→                    .font(.headline)
    62→
    63→                TextField("e.g., My Domain List", text: $sessionName)
    64→                    .textFieldStyle(.roundedBorder)
    65→            }
    66→
    67→            // Progress (when importing)
    68→            if isImporting {
    69→                VStack(spacing: 12) {
    70→                    ProgressView(value: importProgress)
    71→                        .progressViewStyle(.linear)
    72→
    73→                    Text("Importing \(importedCount.formatted()) domains...")
    74→                        .font(.caption)
    75→                        .foregroundStyle(.secondary)
    76→                }
    77→                .padding()
    78→                .background(Color(nsColor: .windowBackgroundColor))
    79→                .clipShape(RoundedRectangle(cornerRadius: 8))
    80→            }
    81→
    82→            // Error
    83→            if let error = errorMessage {
    84→                Text(error)
    85→                    .font(.caption)
    86→                    .foregroundStyle(.red)
    87→                    .padding()
    88→                    .background(Color.red.opacity(0.1))
    89→                    .clipShape(RoundedRectangle(cornerRadius: 8))
    90→            }
    91→
    92→            Spacer()
    93→
    94→            // Actions
    95→            HStack {
    96→                Button("Cancel") {
    97→                    dismiss()
    98→                }
    99→                .keyboardShortcut(.escape)
   100→
   101→                Spacer()
   102→
   103→                Button("Import") {
   104→                    startImport()
   105→                }
   106→                .buttonStyle(.borderedProminent)
   107→                .disabled(selectedFileURL == nil || isImporting)
   108→                .keyboardShortcut(.defaultAction)
   109→            }
   110→        }
   111→        .padding(24)
   112→        .frame(width: 450, height: 450)
   113→        .fileImporter(
   114→            isPresented: $showingFilePicker,
   115→            allowedContentTypes: [.plainText, .commaSeparatedText, UTType(filenameExtension: "csv")!],
   116→            allowsMultipleSelection: false
   117→        ) { result in
   118→            switch result {
   119→            case .success(let urls):
   120→                selectedFileURL = urls.first
   121→                errorMessage = nil
   122→            case .failure(let error):
   123→                errorMessage = error.localizedDescription
   124→            }
   125→        }
   126→    }
   127→
   128→    private func startImport() {
   129→        guard let fileURL = selectedFileURL else { return }
   130→
   131→        isImporting = true
   132→        importProgress = 0
   133→        importedCount = 0
   134→        errorMessage = nil
   135→
   136→        Task {
   137→            do {
   138→                // Start access to security-scoped resource
   139→                let accessing = fileURL.startAccessingSecurityScopedResource()
   140→                defer {
   141→                    if accessing {
   142→                        fileURL.stopAccessingSecurityScopedResource()
   143→                    }
   144→                }
   145→
   146→                let count = await dataManager.importDomains(
   147→                    from: fileURL,
   148→                    sessionName: sessionName.isEmpty ? fileURL.deletingPathExtension().lastPathComponent : sessionName
   149→                )
   150→
   151→                await MainActor.run {
   152→                    importedCount = count
   153→                    isImporting = false
   154→
   155→                    // Success - close after delay
   156→                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
   157→                        dismiss()
   158→                    }
   159→                }
   160→            } catch {
   161→                await MainActor.run {
   162→                    errorMessage = error.localizedDescription
   163→                    isImporting = false
   164→                }
   165→            }
   166→        }
   167→    }
   168→}
   169→
   170→// MARK: - Drop Support
   171→
   172→struct ImportDropDelegate: DropDelegate {
   173→    @Binding var selectedURL: URL?
   174→
   175→    func validateDrop(info: DropInfo) -> Bool {
   176→        info.hasItemsConforming(to: [.plainText, .fileURL])
   177→    }
   178→
   179→    func performDrop(info: DropInfo) -> Bool {
   180→        guard let provider = info.itemProviders(for: [.fileURL]).first else { return false }
   181→
   182→        provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier) { data, error in
   183→            if let data = data as? Data,
   184→               let url = URL(dataRepresentation: data, relativeTo: nil) {
   185→                DispatchQueue.main.async {
   186→                    selectedURL = url
   187→                }
   188→            }
   189→        }
   190→
   191→        return true
   192→    }
   193→}
   194→
   195→#Preview {
   196→    ImportView()
   197→        .environmentObject(DataManager.shared)
   198→}
   199→

<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
import UniformTypeIdentifiers

struct ImportView: View {
    @Environment(\.dismiss) private var dismiss
    @EnvironmentObject var dataManager: DataManager

    @State private var selectedFileURL: URL?
    @State private var sessionName = ""
    @State private var isImporting = false
    @State private var importProgress: Double = 0
    @State private var importedCount = 0
    @State private var errorMessage: String?
    @State private var showingFilePicker = false

    var body: some View {
        VStack(spacing: 24) {
            // Header
            VStack(spacing: 8) {
                Image(systemName: "square.and.arrow.down.fill")
                    .font(.system(size: 48))
                    .foregroundStyle(.blue)

                Text("Import Domains")
                    .font(.title.bold())

                Text("Select a text file containing domain names (one per line)")
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
                    .multilineTextAlignment(.center)
            }

            // File Selection
            VStack(alignment: .leading, spacing: 8) {
                Text("Source File")
                    .font(.headline)

                HStack {
                    if let url = selectedFileURL {
                        Label(url.lastPathComponent, systemImage: "doc.text.fill")
                            .foregroundStyle(.secondary)
                    } else {
                        Text("No file selected")
                            .foregroundStyle(.secondary)
                    }

                    Spacer()

                    Button("Choose File...") {
                        showingFilePicker = true
                    }
                }
                .padding()
                .background(Color(nsColor: .textBackgroundColor))
                .clipShape(RoundedRectangle(cornerRadius: 8))
            }

            // Session Name
            VStack(alignment: .leading, spacing: 8) {
                Text("Session Name (optional)")
                    .font(.headline)

                TextField("e.g., My Domain List", text: $sessionName)
                    .textFieldStyle(.roundedBorder)
            }

            // Progress (when importing)
            if isImporting {
                VStack(spacing: 12) {
                    ProgressView(value: importProgress)
                        .progressViewStyle(.linear)

                    Text("Importing \(importedCount.formatted()) domains...")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                .padding()
                .background(Color(nsColor: .windowBackgroundColor))
                .clipShape(RoundedRectangle(cornerRadius: 8))
            }

            // Error
            if let error = errorMessage {
                Text(error)
                    .font(.caption)
                    .foregroundStyle(.red)
                    .padding()
                    .background(Color.red.opacity(0.1))
                    .clipShape(RoundedRectangle(cornerRadius: 8))
            }

            Spacer()

            // Actions
            HStack {
                Button("Cancel") {
                    dismiss()
                }
                .keyboardShortcut(.escape)

                Spacer()

                Button("Import") {
                    startImport()
                }
                .buttonStyle(.borderedProminent)
                .disabled(selectedFileURL == nil || isImporting)
                .keyboardShortcut(.defaultAction)
            }
        }
        .padding(24)
        .frame(width: 450, height: 450)
        .fileImporter(
            isPresented: $showingFilePicker,
            allowedContentTypes: [.plainText, .commaSeparatedText, UTType(filenameExtension: "csv")!],
            allowsMultipleSelection: false
        ) { result in
            switch result {
            case .success(let urls):
                selectedFileURL = urls.first
                errorMessage = nil
            case .failure(let error):
                errorMessage = error.localizedDescription
            }
        }
    }

    private func startImport() {
        guard let fileURL = selectedFileURL else { return }

        isImporting = true
        importProgress = 0
        importedCount = 0
        errorMessage = nil

        Task {
            do {
                // Start access to security-scoped resource
                let accessing = fileURL.startAccessingSecurityScopedResource()
                defer {
                    if accessing {
                        fileURL.stopAccessingSecurityScopedResource()
                    }
                }

                let count = await dataManager.importDomains(
                    from: fileURL,
                    sessionName: sessionName.isEmpty ? fileURL.deletingPathExtension().lastPathComponent : sessionName
                )

                await MainActor.run {
                    importedCount = count
                    isImporting = false

                    // Success - close after delay
                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                        dismiss()
                    }
                }
            } catch {
                await MainActor.run {
                    errorMessage = error.localizedDescription
                    isImporting = false
                }
            }
        }
    }
}

// MARK: - Drop Support

struct ImportDropDelegate: DropDelegate {
    @Binding var selectedURL: URL?

    func validateDrop(info: DropInfo) -> Bool {
        info.hasItemsConforming(to: [.plainText, .fileURL])
    }

    func performDrop(info: DropInfo) -> Bool {
        guard let provider = info.itemProviders(for: [.fileURL]).first else { return false }

        provider.loadItem(forTypeIdentifier: UTType.fileURL.identifier) { data, error in
            if let data = data as? Data,
               let url = URL(dataRepresentation: data, relativeTo: nil) {
                DispatchQueue.main.async {
                    selectedURL = url
                }
            }
        }

        return true
    }
}

#Preview {
    ImportView()
        .environmentObject(DataManager.shared)
}