import BackendEnvironmentClient
import ComponentLibrary
import ComposableArchitecture
import Foundation
import Localization
import MessageUI
import Photos
import StatsigClient
import SwiftUI
import TikTokOpenShareSDK

private extension Bundle {
    var facebookAppID: String {
        infoDictionary?["FACEBOOK_APP_ID"] as? String ?? "478115168068797"
    }
}

public struct ShareAssetDestination: Identifiable, Equatable {
    public struct ShareData {
        let shareURL: URL
        let stickerData: Data
        let downloadableURL: URL?
        let linkMessage: String

        public init(shareURL: URL, stickerData: Data, downloadableURL: URL?, linkMessage: String) {
            self.shareURL = shareURL
            self.stickerData = stickerData
            self.downloadableURL = downloadableURL
            self.linkMessage = linkMessage
        }
    }

    public enum SharePresence {
        case systemLink
        case systemCopy
        case systemEmail
        case external(((URL) -> Bool) -> Bool)

        public func isAvailable(_ canOpenURL: (URL) -> Bool) -> Bool {
            guard case let .external(isAvailable) = self else { return true }
            return isAvailable(canOpenURL)
        }

        public var isSystemLink: Bool {
            guard case .systemLink = self else { return false }
            return true
        }
    }

    public let id = UUID()
    /// like `reddit`, `facebook`, `x`. used to for share link tracking attribution
    public let platformName: String
    let name: String
    let icon: Image
    let needsVideo: Bool
    public var presence: SharePresence
    public var requiresWatermark: Bool
    public var isDownloadEvent: Bool
    public var share: (ShareData) async throws -> String?

    public static func == (lhs: ShareAssetDestination, rhs: ShareAssetDestination) -> Bool {
        lhs.id == rhs.id
    }
}

enum SharingError: LocalizedError {
    case invalidShareId
    case invalidURL
    case watermarkingFailed
    case requiredPhotosLibraryAccessMissing
    case saveVideoToPhotosLibraryFailed

    var errorDescription: String? {
        switch self {
        case .invalidShareId: "Invalid share ID"
        case .invalidURL: "Invalid share url"
        case .requiredPhotosLibraryAccessMissing: "Required photos library access missing"
        case .saveVideoToPhotosLibraryFailed: "Failed to save video to photos library"
        case .watermarkingFailed: "Failed to save shareable video"
        }
    }
}

public extension ShareAssetDestination {
    static let instagram = ShareAssetDestination(
        platformName: "instagram",
        name: L10n.FeatureShare.instagram,
        icon: Image.Icon.instaShare,
        needsVideo: true,
        presence: .external { canOpenURL in
            let facebookAppID = Bundle.main.facebookAppID
            guard let url = URL(string: "instagram-stories://share?source_application=\(facebookAppID)") else {
                return false
            }
            return canOpenURL(url)
        },
        requiresWatermark: true,
        isDownloadEvent: false,
        share: { @MainActor shareData in

            let faceBookAppID = Bundle.main.facebookAppID
            guard let url = URL(string: "instagram-stories://share?source_application=\(faceBookAppID)") else { throw SharingError.invalidURL }

            prepareInstagram(shareData: shareData)
            await UIApplication.shared.open(url)
            return nil

            func prepareInstagram(shareData: ShareData) {
                var videoData: Data {
                    shareData.downloadableURL.flatMap { try? Data(contentsOf: $0) } ?? .init()
                }
                let pasteBoardItems: [String: Any] = [
                    "com.instagram.sharedSticker.stickerImage": shareData.stickerData,
                    "com.instagram.sharedSticker.backgroundVideo": videoData,
                    "com.instagram.sharedSticker.appID": faceBookAppID,
                    "com.instagram.sharedSticker.linkURL": shareData.shareURL.absoluteString,
                    "com.instagram.sharedSticker.linkText": shareData.linkMessage,
                ]
                let pasteboardOptions = [
                    UIPasteboard.OptionsKey.expirationDate: Date().addingTimeInterval(300),
                ]
                UIPasteboard.general.setItems([pasteBoardItems], options: pasteboardOptions)
            }
        }
    )

    static let facebook = ShareAssetDestination(
        platformName: "facebook",
        name: L10n.FeatureShare.facebook,
        icon: Image.Icon.facebookShare,
        needsVideo: true,
        presence: .external { canOpenURL in
            let facebookAppID = Bundle.main.facebookAppID
            guard let url = URL(string: "facebook-stories://share?source_application=\(facebookAppID)") else {
                return false
            }
            return canOpenURL(url)
        },
        requiresWatermark: true,
        isDownloadEvent: false,
        share: { @MainActor shareData in
            let faceBookAppID = Bundle.main.facebookAppID
            guard let url = URL(string: "facebook-stories://share?source_application=\(faceBookAppID)") else { throw SharingError.invalidURL }

            prepareFacebook(shareData: shareData)
            await UIApplication.shared.open(url)
            return nil

            func prepareFacebook(shareData: ShareData) {
                var videoData: Data {
                    shareData.downloadableURL.flatMap { try? Data(contentsOf: $0) } ?? .init()
                }
                let pasteBoardItems: [String: Any] = [
                    "com.facebook.sharedSticker.stickerImage": shareData.stickerData,
                    "com.facebook.sharedSticker.backgroundVideo": videoData,
                    "com.facebook.sharedSticker.appID": faceBookAppID,
                ]
                let pasteboardOptions = [
                    UIPasteboard.OptionsKey.expirationDate: Date().addingTimeInterval(300),
                ]
                UIPasteboard.general.setItems([pasteBoardItems], options: pasteboardOptions)
            }
        }
    )

    static let download = ShareAssetDestination(
        platformName: "ios_native_download",
        name: L10n.FeatureShare.save,
        icon: Image.Icon.downloadTray,
        needsVideo: true,
        presence: .external { _ in true },
        requiresWatermark: true,
        isDownloadEvent: true,
        share: { @MainActor shareData in
            guard
                let outputURL = shareData.downloadableURL
            else { throw SharingError.invalidURL }

            let videoDownloader = VideoDownloader()
            _ = try await videoDownloader.saveVideo(from: outputURL)

            return L10n.FeatureShare.downloadComplete
        }
    )
    static let tiktok = ShareAssetDestination(
        platformName: "tiktok",
        name: L10n.FeatureShare.tiktok,
        icon: Image.Icon.tiktokShare,
        needsVideo: true,
        presence: .external { canOpenURL in
            guard let url = URL(string: "snssdk1180://") else { return false }
            return canOpenURL(url)
        },
        requiresWatermark: true,
        isDownloadEvent: false,
        share: { @MainActor shareData in
            guard let videoURL = shareData.downloadableURL else { throw SharingError.invalidURL }

            let videoDownloader = VideoDownloader()
            let localIdentifier = try await videoDownloader.saveVideo(from: videoURL)

            let baseUrl = BackendEnvironmentProvider.currentConfiguration().webEndpointHost
            let redirectURI = "https://\(baseUrl)/tiktok"

            let tikTokRequest = TikTokShareRequest(
                localIdentifiers: [localIdentifier],
                mediaType: .video,
                redirectURI: redirectURI
            )

            return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<String?, Error>) in
                tikTokRequest.send { response in
                    guard let shareResponse = response as? TikTokShareResponse else {
                        return continuation.resume(throwing: SharingError.saveVideoToPhotosLibraryFailed)
                    }
                    if shareResponse.errorCode == .noError {
                        continuation.resume(returning: nil)
                    } else {
                        continuation.resume(throwing: SharingError.saveVideoToPhotosLibraryFailed)
                    }
                }
            }
        }
    )
}
