//
//  AttachmentThumbnailController.swift
//  ComposeUI
//
//  Created by Shadowfacts on 11/10/21.
//  Copyright © 2021 Shadowfacts. All rights reserved.
//

import SwiftUI
import Photos
import TuskerComponents

class AttachmentThumbnailController: ViewController {
    unowned let parent: ComposeController
    let attachment: DraftAttachment
    
    @Published private var image: UIImage?
    @Published private var gifController: GIFController?
    @Published private var fullSize: Bool = false
    
    init(attachment: DraftAttachment, parent: ComposeController) {
        self.attachment = attachment
        self.parent = parent
    }
    
    func loadImageIfNecessary(fullSize: Bool) {
        if (gifController != nil) || (image != nil && self.fullSize) {
            return
        }
        self.fullSize = fullSize
        
        switch attachment.data {
        case .editing(_, let kind, let url):
            switch kind {
            case .image:
                Task { @MainActor in
                    self.image = await parent.fetchAttachment(url)
                }
                
            case .video, .gifv:
                let asset = AVURLAsset(url: url)
                let imageGenerator = AVAssetImageGenerator(asset: asset)
                if let cgImage = try? imageGenerator.copyCGImage(at: .zero, actualTime: nil) {
                    self.image = UIImage(cgImage: cgImage)
                }

            case .audio, .unknown:
                break
            }
            
        case .asset(let id):
            guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [id], options: nil).firstObject else {
                return
            }
            let isGIF = PHAssetResource.assetResources(for: asset).contains(where: { $0.uniformTypeIdentifier == UTType.gif.identifier })
            if isGIF {
                PHImageManager.default().requestImageDataAndOrientation(for: asset, options: nil) { data, typeIdentifier, orientation, info in
                    guard let data else { return }
                    if typeIdentifier == UTType.gif.identifier {
                        self.gifController = GIFController(gifData: data)
                    } else {
                        let image = UIImage(data: data)
                        DispatchQueue.main.async {
                            self.image = image
                        }
                    }
                }
            } else {
                let size: CGSize
                if fullSize {
                    size = PHImageManagerMaximumSize
                } else {
                    // currently only used as thumbnail in ComposeAttachmentRow
                    size = CGSize(width: 80, height: 80)
                }
                PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: nil) { (image, _) in
                    DispatchQueue.main.async {
                        self.image = image
                    }
                }
            }
            
        case .drawing(let drawing):
            image = drawing.imageInLightMode(from: drawing.bounds)

        case .file(let url, let type):
            if type.conforms(to: .movie) {
                let asset = AVURLAsset(url: url)
                let imageGenerator = AVAssetImageGenerator(asset: asset)
                if let cgImage = try? imageGenerator.copyCGImage(at: .zero, actualTime: nil) {
                    self.image = UIImage(cgImage: cgImage)
                }
            } else if let data = try? Data(contentsOf: url) {
                if type == .gif {
                    self.gifController = GIFController(gifData: data)
                } else if type.conforms(to: .image),
                   let image = UIImage(data: data) {
                    // using prepareThumbnail on images from PHPicker results in extremely high memory usage,
                    // crashing share extension. see FB12186346
//                    if fullSize {
                        image.prepareForDisplay { prepared in
                            DispatchQueue.main.async {
                                self.image = image
                            }
                        }
//                    } else {
//                        image.prepareThumbnail(of: CGSize(width: 80, height: 80)) { prepared in
//                            DispatchQueue.main.async {
//                                self.image = prepared
//                            }
//                        }
//                    }
                }
            }
            
        case .none:
            break
        }
    }
    
    var view: some SwiftUI.View {
        View()
    }
    
    struct View: SwiftUI.View {
        @EnvironmentObject private var controller: AttachmentThumbnailController
        @Environment(\.attachmentThumbnailConfiguration) private var config
        
        var body: some SwiftUI.View {
            content
                .onAppear {
                    controller.loadImageIfNecessary(fullSize: config.fullSize)
                }
        }
        
        @ViewBuilder
        private var content: some SwiftUI.View {
            if let gifController = controller.gifController {
                GIFViewWrapper(controller: gifController)
            } else if let image = controller.image {
                Image(uiImage: image)
                    .resizable()
                    .aspectRatio(config.aspectRatio, contentMode: config.contentMode)
            } else {
                Image(systemName: "photo")
            }
        }
    }
}

struct AttachmentThumbnailConfiguration {
    let aspectRatio: CGFloat?
    let contentMode: ContentMode
    let fullSize: Bool
    
    init(aspectRatio: CGFloat? = nil, contentMode: ContentMode = .fit, fullSize: Bool = false) {
        self.aspectRatio = aspectRatio
        self.contentMode = contentMode
        self.fullSize = fullSize
    }
}

private struct AttachmentThumbnailConfigurationEnvironmentKey: EnvironmentKey {
    static let defaultValue = AttachmentThumbnailConfiguration()
}

extension EnvironmentValues {
    var attachmentThumbnailConfiguration: AttachmentThumbnailConfiguration {
        get { self[AttachmentThumbnailConfigurationEnvironmentKey.self] }
        set { self[AttachmentThumbnailConfigurationEnvironmentKey.self] = newValue }
    }
}

private struct GIFViewWrapper: UIViewRepresentable {
    typealias UIViewType = GIFImageView

    @State var controller: GIFController

    func makeUIView(context: Context) -> GIFImageView {
        let view = GIFImageView()
        controller.attach(to: view)
        controller.startAnimating()
        view.contentMode = .scaleAspectFit
        view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        view.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
        return view
    }

    func updateUIView(_ uiView: GIFImageView, context: Context) {
    }
}
