import Adamantium
import ComposableArchitecture
import CoreImage
import Foundation

public struct AdamantiumClient {
    public var configure: @Sendable () async -> Void

    // (input video url, image mark) -> output url
    public var watermarkVideo: @Sendable (_ videoURL: URL?, _ imageMark: CIImage, _ isImageAsVideo: Bool) async throws -> URL?
    public var retimeVideo: @Sendable (_ videoURL: URL?, _ fromFPS: Float, _ toFPS: Float) async throws -> URL?
    public var getCacheResource: @Sendable (_ resourceID: AdamantiumAppResource) -> URL?
}

extension AdamantiumClient: DependencyKey {
    public static let liveValue: Self = {
        let sharedCache = AdamantiumResourceCache()

        return Self(
            configure: { @MainActor in
                #if targetEnvironment(simulator)
                /** Nothing */
                #else
                    /**
                        - Get access to GPU
                        - Setup Shared instance of TextureLoader
                        - Setup Shader Library
                        - Setup Shared CommandQueue
                     */
                    Adamantium.configure()

                    /**
                         Setup prerendered resources cache
                             - Includes prerendered aura videos
                             - ... More anticipated
                     */
                    do {
                        try await AdamantiumAppResource.setupCacheIfNeeded(sharedCache)
                    } catch {
                        log.error("Failed to set up cache: \(error.localizedDescription)")
                    }
                #endif
            },
            watermarkVideo: { videoURL, imageMark, isImageAsVideo in
                guard let videoURL else { return nil }
                let markTransform: VideoWatermarkUtility.WatermarkTransform = .init(
                    opacity: 1.0,
                    widthRatioScale: .init(width: 0.2, height: 0.2),
                    widthRatioTranslation: .init(x: 0.05, y: 0.4)
                )

                if isImageAsVideo {
                    // Make fast format video for images case
                    let markedVideoURL = try await VideoWatermarkUtility
                        .watermarkImageVideo(inputURL: videoURL, markImage: imageMark, markTransform: markTransform)
                    return markedVideoURL

                } else {
                    // Go through every frame
                    let markedVideoSession = try await VideoWatermarkUtility
                        .createWatermarkedAssetForFirstNSeconds(
                            inputURL: videoURL,
                            markImage: imageMark,
                            markTransform: markTransform,
                            markDuration: .init(seconds: 5.0, preferredTimescale: 600),
                            markFadeOutDuration: 1.0
                        )
                    guard
                        case .completed = markedVideoSession?.status,
                        let outputURL = markedVideoSession?.outputURL
                    else { return videoURL }
                    return outputURL
                }
            },
            retimeVideo: { videoURL, fromFPS, toFPS in
                guard let videoURL else { return nil }
                return try await VideoRetimeUtility.scaleFrameRate(of: videoURL, from: fromFPS, to: toFPS)
            },
            getCacheResource: { resourceID in
                sharedCache.getCachedFile(for: resourceID.cacheKey)
            }
        )
    }()
}

extension AdamantiumClient: TestDependencyKey {
    public static let previewValue = Self.noop

    public static let testValue = Self(
        configure: {},
        watermarkVideo: { _, _, _ in nil },
        retimeVideo: { _, _, _ in nil },
        getCacheResource: { _ in nil }
    )
}

public extension AdamantiumClient {
    static let noop = Self(
        configure: unimplemented("\(Self.self).configure"),
        watermarkVideo: unimplemented("\(Self.self).watermarkVideo"),
        retimeVideo: unimplemented("\(Self.self).retimeVideo"),
        getCacheResource: unimplemented("\(Self.self).retimeVideo", placeholder: URL(string: "https://suno.com")!)
    )

    static let failing = Self(
        configure: {},
        watermarkVideo: { _, _, _ in throw NSError.mock() },
        retimeVideo: { _, _, _ in throw NSError.mock() },
        getCacheResource: { _ in nil }
    )
}

// MARK: - NSError Helper

private extension NSError {
    static func mock() -> NSError {
        NSError(domain: "AdamantiumClient", code: -1)
    }
}
