import Dependencies
import Localization
import PhotosUI
import SwiftUI
import Utilities

struct PHPickerViewRepresentable: UIViewControllerRepresentable {
    @Dependency(\.telemetryClient) var telemetry

    let completion: (Result<ImagePickerResult, Error>) -> Void
    let cancel: () -> Void

    struct ImagePickerResult: Equatable {
        let image: UIImage
        let suggestedName: String?
    }

    enum ImagePickerError: Error, LocalizedError {
        case cannotLoadPhoto
        case unknownError

        var errorDescription: String? {
            switch self {
            case .cannotLoadPhoto: L10n.FeaturePhotoPicker.photoLoadingError
            case .unknownError: L10n.FeaturePhotoPicker.unknownError
            }
        }
    }

    func makeUIViewController(context: Context) -> some UIViewController {
        var configuration = PHPickerConfiguration(photoLibrary: .shared())
        configuration.selectionLimit = 1
        configuration.preferredAssetRepresentationMode = .current

        let controller = PHPickerViewController(configuration: configuration)
        controller.delegate = context.coordinator

        context.coordinator.downloadHandler = { downloading in
            if downloading {
                UIApplication.shared.topViewController?.showLoadingView()
            } else {
                UIApplication.shared.topViewController?.hideLoadingView()
            }
        }
        context.coordinator.errorHandler = { error in
            let alert = UIAlertController(title: L10n.FeaturePhotoPicker.photoDownloadError, message: error.localizedDescription, preferredStyle: .alert)
            let action = UIAlertAction(title: L10n.FeaturePhotoPicker.dismiss, style: .default)
            alert.addAction(action)

            UIApplication.shared.topViewController?.hideLoadingView()
            UIApplication.shared.topViewController?.present(alert, animated: true)

            log.telemetry.error(error)
        }

        return controller
    }

    func updateUIViewController(_: UIViewControllerType, context _: Context) {}

    func makeCoordinator() -> Coordinator {
        Coordinator(completion: completion, cancel: cancel)
    }

    class Coordinator: NSObject, PHPickerViewControllerDelegate {
        let completion: (Result<ImagePickerResult, Error>) -> Void
        let cancel: () -> Void

        var downloadHandler: ((Bool) -> Void)?
        var errorHandler: ((Error) -> Void)?

        init(
            completion: @escaping (Result<ImagePickerResult, Error>) -> Void,
            cancel: @escaping () -> Void
        ) {
            self.completion = completion
            self.cancel = cancel
        }

        func picker(_: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
            guard let assetIdentifier = results.first?.assetIdentifier else {
                cancel()
                return
            }

            let assetResults = PHAsset.fetchAssets(withLocalIdentifiers: [assetIdentifier], options: nil)

            guard let asset = assetResults.firstObject else {
                DispatchQueue.main.async {
                    self.completion(.failure(ImagePickerError.cannotLoadPhoto))
                    self.errorHandler?(ImagePickerError.cannotLoadPhoto)
                }
                return
            }

            if asset.isLocallyAvailable == false {
                DispatchQueue.main.async {
                    self.downloadHandler?(true)
                }
            }

            let options = PHImageRequestOptions()
            options.deliveryMode = .opportunistic
            options.isSynchronous = true
            options.isNetworkAccessAllowed = true

            let targetSize = CGSize(width: 1024, height: 1024)

            PHImageManager.default().requestImage(
                for: asset,
                targetSize: targetSize,
                contentMode: .aspectFit,
                options: options
            ) { [weak self] image, info in
                guard let self = self else { return }

                DispatchQueue.main.async {
                    if let image = image {
                        self.completion(.success(.init(image: image, suggestedName: asset.localIdentifier)))
                    } else if let error = info?[PHImageErrorKey] as? NSError {
                        self.completion(.failure(error))
                        self.errorHandler?(error)
                    } else {
                        self.completion(.failure(ImagePickerError.unknownError))
                        self.errorHandler?(ImagePickerError.unknownError)
                    }

                    self.downloadHandler?(false)
                }
            }
        }
    }
}

extension PHAsset {
    var isLocallyAvailable: Bool? {
        let resourceArray = PHAssetResource.assetResources(for: self)
        return resourceArray.first?.value(forKey: "locallyAvailable") as? Bool
    }
}

final class LoadingView: UIView {
    private let activityIndicatorView: UIActivityIndicatorView = {
        let view = UIActivityIndicatorView(style: .large)
        view.translatesAutoresizingMaskIntoConstraints = false
        view.color = .white
        return view
    }()

    override func layoutSubviews() {
        super.layoutSubviews()

        backgroundColor = UIColor.black.withAlphaComponent(0.75)
        layer.cornerRadius = 5

        if activityIndicatorView.superview == nil {
            addSubview(activityIndicatorView)

            NSLayoutConstraint.activate([
                activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor),
                activityIndicatorView.centerYAnchor.constraint(equalTo: centerYAnchor),
            ])

            activityIndicatorView.startAnimating()
        }
    }

    public func startAnimating() {
        activityIndicatorView.startAnimating()
    }

    public func stopAnimating() {
        activityIndicatorView.stopAnimating()
    }
}

extension UIViewController {
    func showLoadingView() {
        let loadingView = LoadingView()
        loadingView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(loadingView)

        NSLayoutConstraint.activate([
            loadingView.widthAnchor.constraint(equalToConstant: 80),
            loadingView.heightAnchor.constraint(equalToConstant: 80),
            loadingView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            loadingView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])

        loadingView.startAnimating()

        view.bringSubviewToFront(loadingView)
    }

    func hideLoadingView() {
        for subview in view.subviews {
            if let loadingView = subview as? LoadingView {
                loadingView.stopAnimating()
                loadingView.removeFromSuperview()
            }
        }
    }
}
