using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using Bit.Core.Resources.Localization;
using Bit.Core.Abstractions;
using Bit.iOS.Core.Utilities;
using Foundation;
using MobileCoreServices;
using Photos;
using UIKit;

namespace Bit.iOS.Core.Services
{
    public class FileService : IFileService
    {
        private readonly IStateService _stateService;
        private readonly IMessagingService _messagingService;

        public FileService(IStateService stateService, IMessagingService messagingService)
        {
            _stateService = stateService;
            _messagingService = messagingService;
        }

        public bool OpenFile(byte[] fileData, string id, string fileName)
        {
            var filePath = Path.Combine(GetTempPath(), fileName);
            File.WriteAllBytes(filePath, fileData);
            var url = NSUrl.FromFilename(filePath);
            var controller = UIViewControllerExtensions.GetVisibleViewController();

            try
            {
                UIView presentingView = UIApplication.SharedApplication.KeyWindow.RootViewController.View;
                var documentController = new UIDocumentPickerViewController(url, UIDocumentPickerMode.ExportToService);
                controller.PresentViewController(documentController, true, null);

                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool CanOpenFile(string fileName)
        {
            // Not sure of a way to check this ahead of time on iOS
            return true;
        }

        public bool SaveFile(byte[] fileData, string id, string fileName, string contentUri)
        {
            // OpenFile behavior is appropriate here as iOS prompts to save file
            return OpenFile(fileData, id, fileName);
        }

        public async Task ClearCacheAsync()
        {
            var url = new NSUrl(GetTempPath());
            var tmpFiles = NSFileManager.DefaultManager.GetDirectoryContent(url, null,
                NSDirectoryEnumerationOptions.SkipsHiddenFiles, out NSError error);
            if (error == null && tmpFiles.Length > 0)
            {
                foreach (var item in tmpFiles)
                {
                    NSFileManager.DefaultManager.Remove(item, out NSError itemError);
                }
            }
            await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
        }

        public Task SelectFileAsync()
        {
            var controller = UIViewControllerExtensions.GetVisibleViewController();
            var picker = new UIDocumentMenuViewController(new string[] { UTType.Data }, UIDocumentPickerMode.Import);
            picker.AddOption(AppResources.Camera, UIImage.FromBundle("camera"), UIDocumentMenuOrder.First, () =>
            {
                var imagePicker = new UIImagePickerController
                {
                    SourceType = UIImagePickerControllerSourceType.Camera
                };
                imagePicker.FinishedPickingMedia += ImagePicker_FinishedPickingMedia;
                imagePicker.Canceled += ImagePicker_Canceled;
                controller.PresentModalViewController(imagePicker, true);
            });
            picker.AddOption(AppResources.Photos, UIImage.FromBundle("photo"), UIDocumentMenuOrder.First, () =>
            {
                var imagePicker = new UIImagePickerController
                {
                    SourceType = UIImagePickerControllerSourceType.PhotoLibrary
                };
                imagePicker.FinishedPickingMedia += ImagePicker_FinishedPickingMedia;
                imagePicker.Canceled += ImagePicker_Canceled;
                controller.PresentModalViewController(imagePicker, true);
            });
            picker.DidPickDocumentPicker += (sender, e) =>
            {
                if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
                {
                    e.DocumentPicker.Delegate = new PickerDelegate(this);
                }
                else
                {
                    e.DocumentPicker.DidPickDocument += DocumentPicker_DidPickDocument;
                }
                controller.PresentViewController(e.DocumentPicker, true, null);
            };
            var root = UIApplication.SharedApplication.KeyWindow.RootViewController;
            if (picker.PopoverPresentationController != null && root != null)
            {
                picker.PopoverPresentationController.SourceView = root.View;
                picker.PopoverPresentationController.SourceRect = root.View.Bounds;
            }
            controller.PresentViewController(picker, true, null);
            return Task.CompletedTask;
        }

        // ref:  //https://developer.xamarin.com/guides/ios/application_fundamentals/working_with_the_file_system/
        public string GetTempPath()
        {
            var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            return Path.Combine(documents, "..", "tmp");
        }

        private void ImagePicker_FinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
        {
            if (sender is UIImagePickerController picker)
            {
                string fileName = null;
                if (e.Info.TryGetValue(UIImagePickerController.ReferenceUrl, out NSObject urlObj))
                {
                    var result = PHAsset.FetchAssets(new NSUrl[] { (urlObj as NSUrl) }, null);
                    fileName = result?.firstObject?.ValueForKey(new NSString("filename"))?.ToString();
                }
                fileName = fileName ?? $"photo_{DateTime.UtcNow.ToString("yyyyMMddHHmmss")}.jpg";
                var lowerFilename = fileName?.ToLowerInvariant();
                byte[] data;
                if (lowerFilename != null && (lowerFilename.EndsWith(".jpg") || lowerFilename.EndsWith(".jpeg")))
                {
                    using (var imageData = e.OriginalImage.AsJPEG())
                    {
                        data = new byte[imageData.Length];
                        System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, data, 0,
                            Convert.ToInt32(imageData.Length));
                    }
                }
                else
                {
                    using (var imageData = e.OriginalImage.AsPNG())
                    {
                        data = new byte[imageData.Length];
                        System.Runtime.InteropServices.Marshal.Copy(imageData.Bytes, data, 0,
                            Convert.ToInt32(imageData.Length));
                    }
                }
                SelectFileResult(data, fileName);
                picker.DismissViewController(true, null);
            }
        }

        private void ImagePicker_Canceled(object sender, EventArgs e)
        {
            if (sender is UIImagePickerController picker)
            {
                picker.DismissViewController(true, null);
            }
        }

        private void DocumentPicker_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
        {
            PickedDocument(e.Url);
        }

        public void PickedDocument(NSUrl url)
        {
            url.StartAccessingSecurityScopedResource();
            var doc = new UIDocument(url);
            var fileName = doc.LocalizedName;
            if (string.IsNullOrWhiteSpace(fileName))
            {
                var path = doc.FileUrl?.ToString();
                if (path != null)
                {
                    path = WebUtility.UrlDecode(path);
                    var split = path.LastIndexOf('/');
                    fileName = path.Substring(split + 1);
                }
            }
            var fileCoordinator = new NSFileCoordinator();
            fileCoordinator.CoordinateRead(url, NSFileCoordinatorReadingOptions.WithoutChanges,
                out NSError error, (u) =>
                {
                    var data = NSData.FromUrl(u).ToArray();
                    SelectFileResult(data, fileName ?? "unknown_file_name");
                });
            url.StopAccessingSecurityScopedResource();
        }

        private void SelectFileResult(byte[] data, string fileName)
        {
            _messagingService.Send("selectFileResult", new Tuple<byte[], string>(data, fileName));
        }

        public class PickerDelegate : UIDocumentPickerDelegate
        {
            private readonly FileService _fileService;

            public PickerDelegate(FileService fileService)
            {
                _fileService = fileService;
            }

            public override void DidPickDocument(UIDocumentPickerViewController controller, NSUrl url)
            {
                _fileService.PickedDocument(url);
            }
        }
    }
}
