using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Android;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Provider;
using Android.Webkit;
using AndroidX.Core.App;
using AndroidX.Core.Content;
using Bit.App.Resources;
using Bit.Core;
using Bit.Core.Abstractions;
using Plugin.CurrentActivity;

namespace Bit.Droid.Services
{
    public class FileService : IFileService
    {
        private readonly IStateService _stateService;
        private readonly IBroadcasterService _broadcasterService;

        private bool _cameraPermissionsDenied;

        public FileService(IStateService stateService, IBroadcasterService broadcasterService)
        {
            _stateService = stateService;
            _broadcasterService = broadcasterService;

            _broadcasterService.Subscribe(nameof(FileService), (message) =>
            {
                if (message.Command == "selectFileCameraPermissionDenied")
                {
                    _cameraPermissionsDenied = true;
                }
            });
        }

        public bool OpenFile(byte[] fileData, string id, string fileName)
        {
            try
            {
                var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
                var intent = BuildOpenFileIntent(fileData, fileName);
                if (intent == null)
                {
                    return false;
                }
                activity.StartActivity(intent);
                return true;
            }
            catch { }
            return false;
        }

        public bool CanOpenFile(string fileName)
        {
            try
            {
                var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
                var intent = BuildOpenFileIntent(new byte[0], string.Concat("opentest_", fileName));
                if (intent == null)
                {
                    return false;
                }
                var activities = activity.PackageManager.QueryIntentActivities(intent,
                    PackageInfoFlags.MatchDefaultOnly);
                return (activities?.Count ?? 0) > 0;
            }
            catch { }
            return false;
        }

        private Intent BuildOpenFileIntent(byte[] fileData, string fileName)
        {
            var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
            if (extension == null)
            {
                return null;
            }
            var mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
            if (mimeType == null)
            {
                return null;
            }

            var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
            var cachePath = activity.CacheDir;
            var filePath = Path.Combine(cachePath.Path, fileName);
            File.WriteAllBytes(filePath, fileData);
            var file = new Java.IO.File(cachePath, fileName);
            if (!file.IsFile)
            {
                return null;
            }

            try
            {
                var intent = new Intent(Intent.ActionView);
                var uri = FileProvider.GetUriForFile(activity.ApplicationContext,
                    "com.x8bit.bitwarden.fileprovider", file);
                intent.SetDataAndType(uri, mimeType);
                intent.SetFlags(ActivityFlags.GrantReadUriPermission);
                return intent;
            }
            catch { }
            return null;
        }

        public bool SaveFile(byte[] fileData, string id, string fileName, string contentUri)
        {
            try
            {
                var activity = (MainActivity)CrossCurrentActivity.Current.Activity;

                if (contentUri != null)
                {
                    var uri = Android.Net.Uri.Parse(contentUri);
                    var stream = activity.ContentResolver.OpenOutputStream(uri);
                    // Using java bufferedOutputStream due to this issue:
                    // https://github.com/xamarin/xamarin-android/issues/3498
                    var javaStream = new Java.IO.BufferedOutputStream(stream);
                    javaStream.Write(fileData);
                    javaStream.Flush();
                    javaStream.Close();
                    return true;
                }

                // Prompt for location to save file
                var extension = MimeTypeMap.GetFileExtensionFromUrl(fileName.Replace(' ', '_').ToLower());
                if (extension == null)
                {
                    return false;
                }

                string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
                if (mimeType == null)
                {
                    // Unable to identify so fall back to generic "any" type
                    mimeType = "*/*";
                }

                var intent = new Intent(Intent.ActionCreateDocument);
                intent.SetType(mimeType);
                intent.AddCategory(Intent.CategoryOpenable);
                intent.PutExtra(Intent.ExtraTitle, fileName);

                activity.StartActivityForResult(intent, Core.Constants.SaveFileRequestCode);
                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace);
            }
            return false;
        }

        public async Task ClearCacheAsync()
        {
            try
            {
                DeleteDir(CrossCurrentActivity.Current.Activity.CacheDir);
                await _stateService.SetLastFileCacheClearAsync(DateTime.UtcNow);
            }
            catch (Exception) { }
        }

        public Task SelectFileAsync()
        {
            var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
            var hasStorageWritePermission = !_cameraPermissionsDenied &&
                HasPermission(Manifest.Permission.WriteExternalStorage);
            var additionalIntents = new List<IParcelable>();
            if (activity.PackageManager.HasSystemFeature(PackageManager.FeatureCamera))
            {
                var hasCameraPermission = !_cameraPermissionsDenied && HasPermission(Manifest.Permission.Camera);
                if (!_cameraPermissionsDenied && !hasStorageWritePermission)
                {
                    AskPermission(Manifest.Permission.WriteExternalStorage);
                    return Task.FromResult(0);
                }
                if (!_cameraPermissionsDenied && !hasCameraPermission)
                {
                    AskPermission(Manifest.Permission.Camera);
                    return Task.FromResult(0);
                }
                if (!_cameraPermissionsDenied && hasCameraPermission && hasStorageWritePermission)
                {
                    try
                    {
                        var file = new Java.IO.File(activity.FilesDir, "temp_camera_photo.jpg");
                        if (!file.Exists())
                        {
                            file.ParentFile.Mkdirs();
                            file.CreateNewFile();
                        }
                        var outputFileUri = FileProvider.GetUriForFile(activity,
                            "com.x8bit.bitwarden.fileprovider", file);
                        additionalIntents.AddRange(GetCameraIntents(outputFileUri));
                    }
                    catch (Java.IO.IOException) { }
                }
            }

            var docIntent = new Intent(Intent.ActionOpenDocument);
            docIntent.AddCategory(Intent.CategoryOpenable);
            docIntent.SetType("*/*");
            var chooserIntent = Intent.CreateChooser(docIntent, AppResources.FileSource);
            if (additionalIntents.Count > 0)
            {
                chooserIntent.PutExtra(Intent.ExtraInitialIntents, additionalIntents.ToArray());
            }
            activity.StartActivityForResult(chooserIntent, Core.Constants.SelectFileRequestCode);
            return Task.FromResult(0);
        }

        private bool DeleteDir(Java.IO.File dir)
        {
            if (dir is null)
            {
                return false;
            }

            if (dir.IsDirectory)
            {
                var children = dir.List();
                for (int i = 0; i < children.Length; i++)
                {
                    var success = DeleteDir(new Java.IO.File(dir, children[i]));
                    if (!success)
                    {
                        return false;
                    }
                }
                return dir.Delete();
            }

            if (dir.IsFile)
            {
                return dir.Delete();
            }

            return false;
        }

        private bool HasPermission(string permission)
        {
            return ContextCompat.CheckSelfPermission(
                CrossCurrentActivity.Current.Activity, permission) == Permission.Granted;
        }

        private void AskPermission(string permission)
        {
            ActivityCompat.RequestPermissions(CrossCurrentActivity.Current.Activity, new string[] { permission },
                Core.Constants.SelectFilePermissionRequestCode);
        }

        private List<IParcelable> GetCameraIntents(Android.Net.Uri outputUri)
        {
            var intents = new List<IParcelable>();
            var pm = CrossCurrentActivity.Current.Activity.PackageManager;
            var captureIntent = new Intent(MediaStore.ActionImageCapture);
            var listCam = pm.QueryIntentActivities(captureIntent, 0);
            foreach (var res in listCam)
            {
                var packageName = res.ActivityInfo.PackageName;
                var intent = new Intent(captureIntent);
                intent.SetComponent(new ComponentName(packageName, res.ActivityInfo.Name));
                intent.SetPackage(packageName);
                intent.PutExtra(MediaStore.ExtraOutput, outputUri);
                intents.Add(intent);
            }
            return intents;
        }
    }
}
