import ComposableArchitecture
import Foundation
import PhotoLibraryClient
import Photos
import Utilities

public struct VideoDownloader {
    public init() {}

    private let log = Logger(category: "VideoDownloader")
    @Dependency(\.telemetryClient) var telemetry
    @Dependency(PhotoLibraryClient.self) var photoLibraryClient

    public func saveVideo(from videoURL: URL) async throws -> String {
        guard await grantAddOnlyPermissions() else {
            throw VideoDownloaderError.photoLibraryAccessMissing
        }

        var placeholderIdentifier: String?
        var hasReachedContinuation: Bool = false
        return try await withCheckedThrowingContinuation { continuation in
            PHPhotoLibrary.shared().performChanges {
                guard let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoURL) else {
                    return
                }
                // Save as most recent video
                assetChangeRequest.creationDate = Date()

                placeholderIdentifier = assetChangeRequest.placeholderForCreatedAsset?.localIdentifier

            } completionHandler: { success, error in
                guard !hasReachedContinuation else { return }
                hasReachedContinuation = true

                if let error = error {
                    log.telemetry.error(error)
                    continuation.resume(throwing: error)
                } else if success, let identifier = placeholderIdentifier {
                    continuation.resume(returning: identifier)
                } else {
                    continuation.resume(throwing: VideoDownloaderError.saveFailed)
                }
            }
        }
    }

    private func grantAddOnlyPermissions() async -> Bool {
        guard !photoLibraryClient.canSaveToPhotoLibrary() else {
            return true
        }
        return await photoLibraryClient.requestAuthorizationAddOnly() == .authorized
    }
}

public enum VideoDownloaderError: Error {
    case photoLibraryAccessMissing
    case saveFailed
    case saveCancelled
}
