import CloudKit
import Foundation

/// Service for uploading and managing shared cards in CloudKit public database.
struct SharedCardCloudKitService: SharedCardProviding {
    private let container: CKContainer
    private let database: CKDatabase
    private let ttlDays: Int

    init(
        containerID: String = AppIdentifiers.cloudKitContainerIdentifier,
        ttlDays: Int = 7
    ) {
        self.container = CKContainer(identifier: containerID)
        self.database = container.publicCloudDatabase
        self.ttlDays = ttlDays
    }

    @MainActor
    func uploadSharedCard(_ card: BusinessCard) async throws -> SharedCardUploadResult {
        let vCardData = card.vCardFilePayload
        let expiresAt = Date.now.addingTimeInterval(TimeInterval(ttlDays * 24 * 60 * 60))
        let recordID = CKRecord.ID(recordName: UUID().uuidString)
        var photoAssetFileURL: URL?

        let sharedCard = SharedCardRecord(
            recordID: recordID,
            displayName: card.vCardName.isEmpty ? card.fullName : card.vCardName,
            role: card.role,
            company: card.company,
            vCardData: vCardData,
            expiresAt: expiresAt
        )

        do {
            photoAssetFileURL = try makePhotoAssetFile(from: card.photoData)
        } catch {
            throw SharedCardError.uploadFailed(error)
        }

        defer {
            if let photoAssetFileURL {
                try? FileManager.default.removeItem(at: photoAssetFileURL)
            }
        }

        let record = sharedCard.toCKRecord(photoAssetFileURL: photoAssetFileURL)

        do {
            _ = try await database.save(record)
        } catch {
            throw SharedCardError.uploadFailed(error)
        }

        guard let appClipURL = AppIdentifiers.appClipURL(recordName: recordID.recordName) else {
            throw SharedCardError.invalidURL
        }

        return SharedCardUploadResult(
            recordName: recordID.recordName,
            appClipURL: appClipURL,
            expiresAt: expiresAt
        )
    }

    func cleanupExpiredCards() async {
        let predicate = NSPredicate(format: "expiresAt < %@", Date.now as NSDate)
        let query = CKQuery(recordType: SharedCardRecord.recordType, predicate: predicate)

        do {
            let (results, _) = try await database.records(matching: query)
            let recordIDs = results.compactMap { result -> CKRecord.ID? in
                guard case .success = result.1 else { return nil }
                return result.0
            }

            guard !recordIDs.isEmpty else { return }

            let operation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: recordIDs)
            operation.qualityOfService = .utility
            database.add(operation)
        } catch {
            // Cleanup is best-effort; log but don't throw
        }
    }

    private func makePhotoAssetFile(from photoData: Data?) throws -> URL? {
        guard let photoData, !photoData.isEmpty else { return nil }
        let fileURL = URL.temporaryDirectory.appending(path: "\(UUID().uuidString).jpg")
        try photoData.write(to: fileURL, options: .atomic)
        return fileURL
    }
}

// MARK: - Error Types

/// Errors that can occur during shared card operations.
enum SharedCardError: Error, LocalizedError {
    case invalidURL
    case uploadFailed(Error)
    case fetchFailed(Error)
    case recordNotFound
    case recordExpired

    var errorDescription: String? {
        switch self {
        case .invalidURL:
            return String(localized: "Failed to create share URL")
        case .uploadFailed(let error):
            return String(localized: "Upload failed: \(error.localizedDescription)")
        case .fetchFailed(let error):
            return String(localized: "Could not load card: \(error.localizedDescription)")
        case .recordNotFound:
            return String(localized: "Card not found")
        case .recordExpired:
            return String(localized: "This card has expired")
        }
    }
}
