// SPDX-FileCopyrightText: 2026 John Burwell and contributors
// SPDX-License-Identifier: AGPL-3.0-or-later

import Foundation

typealias SourceAccessorIdentifier = String

nonisolated enum SourceAvailability: String, Hashable, Sendable, Codable {
    case unknown
    case available
    case disconnected
    case limited
    case unavailable
}

nonisolated enum SourceRefreshStrategy: String, Hashable, Sendable, Codable {
    case eagerFullScan
    case staged
}

nonisolated struct SourceAccessDescriptor: Hashable, Sendable, Codable {
    var accessorIdentifier: SourceAccessorIdentifier
    var kind: MinecraftSourceKind
    var refreshStrategy: SourceRefreshStrategy
}

nonisolated enum SourceAccessMode: String, Hashable, Sendable, Codable {
    case localFileSystem
    case securityScopedLocalFolder
    case usbDevice
    case networkDevice
    case archive
    case unknown
}

nonisolated struct SourceAccessStatus: Hashable, Sendable, Codable {
    var availability: SourceAvailability
    var mode: SourceAccessMode
    var displayName: String
    var iconSystemName: String
    var statusText: String?
    var warningText: String?
}

nonisolated enum SourceProbeConfidence: Int, Comparable, Hashable, Sendable, Codable {
    case none = 0
    case weak = 25
    case medium = 50
    case strong = 75
    case exact = 100

    static func < (lhs: SourceProbeConfidence, rhs: SourceProbeConfidence) -> Bool {
        lhs.rawValue < rhs.rawValue
    }
}

nonisolated struct SourceProbeResult: Hashable, Sendable {
    let providerID: PlatformProviderID
    let edition: MinecraftEdition
    let confidence: SourceProbeConfidence
    let sourceRootURL: URL
    let displayName: String
    let detectedKinds: Set<MinecraftContentKind>
    let warnings: [String]
}

nonisolated struct SourceCandidate: Identifiable, Hashable, Sendable {
    var providerID: PlatformProviderID
    var edition: MinecraftEdition
    var sourceRootURL: URL
    var displayName: String
    var confidence: SourceProbeConfidence
    var reason: String
    var detectedKinds: Set<MinecraftContentKind>

    var id: String {
        [
            providerID,
            sourceIdentityKey(for: sourceRootURL)
        ].joined(separator: "::")
    }
}

nonisolated func sourceIdentityKey(for url: URL) -> String {
    if url.isFileURL {
        return url.standardizedFileURL.resolvingSymlinksInPath().path.lowercased()
    }

    return url.standardized.absoluteString.lowercased()
}

nonisolated enum WorkStageState: String, Hashable, Sendable, Codable {
    case pending
    case running
    case succeeded
    case failed
    case skipped
    case cancelled
}

nonisolated enum WorkProgress: Hashable, Sendable, Codable {
    case indeterminate
    case fraction(Double)
    case count(completed: Int, total: Int?)
}

nonisolated struct WorkStage: Identifiable, Hashable, Sendable, Codable {
    let id: String
    var title: String
    var detail: String?
    var state: WorkStageState
    var progress: WorkProgress
}

nonisolated struct ProviderWarning: Identifiable, Hashable, Sendable, Codable {
    let id: String
    var message: String
    var detail: String?
}

nonisolated enum ProviderEvent: Sendable {
    case accessStatusChanged(SourceAccessStatus)
    case stageUpdated(WorkStage)
    case discovered(MinecraftContentItem)
    case inspected(MinecraftContentItem)
    case warning(ProviderWarning)
}

nonisolated enum SourceCandidateEvent: Sendable {
    case stageUpdated(WorkStage)
    case candidate(SourceCandidate)
    case warning(ProviderWarning)
}

nonisolated struct SourceRecord: Identifiable, Hashable, Sendable, Codable {
    let id: URL
    var displayName: String
    var rootURL: URL
    var origin: MinecraftSourceOrigin
    var accessDescriptor: SourceAccessDescriptor
    var availability: SourceAvailability
    var lastRefreshDate: Date?
}
