using Blahblah.FlowerApp.Data;
using Microsoft.Extensions.Logging;
using static Blahblah.FlowerApp.Extensions;

namespace Blahblah.FlowerApp;

public abstract class AppContentPage : ContentPage, ILoggerContent
{
    public ILogger Logger { get; } = null!;

    public FlowerDatabase Database { get; } = null!;

    protected AppContentPage(FlowerDatabase database, ILogger logger)
    {
        Database = database;
        Logger = logger;
    }

    protected T GetValue<T>(BindableProperty property)
    {
        return (T)GetValue(property);
    }

    bool hasLoading = true;
    ContentView? loading;

#if IOS
    private async Task DoLoading(bool flag)
#else
    private Task DoLoading(bool flag)
#endif
    {
        if (loading == null && hasLoading)
        {
            try
            {
                loading = (ContentView)FindByName("loading");
            }
            catch
            {
                hasLoading = false;
            }
        }
        if (loading != null)
        {
            if (flag)
            {
#if IOS
                loading.IsVisible = true;
                await loading.FadeTo(1, easing: Easing.CubicOut);
#else
                loading.Opacity = 1;
                loading.IsVisible = true;
#endif
            }
            else
            {
#if IOS
                await loading.FadeTo(0, easing: Easing.CubicIn);
                loading.IsVisible = false;
#else
                loading.IsVisible = false;
                loading.Opacity = 0;
#endif
            }
        }
#if ANDROID
        return Task.CompletedTask;
#endif
    }

    protected Task Loading(bool flag)
    {
        IsBusy = flag;

        if (MainThread.IsMainThread)
        {
            return DoLoading(flag);
        }

        var source = new TaskCompletionSource();
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            await DoLoading(flag);
            source.TrySetResult();
        });
        return source.Task;
    }

    async Task<Location?> GetLastLocationAsyncInternal()
    {
        try
        {
            var location = await Geolocation.Default.GetLastKnownLocationAsync();
            return location;
        }
        catch (FeatureNotSupportedException fnsEx)
        {
            this.LogError(fnsEx, $"Not supported on device, {fnsEx.Message}.");
        }
        catch (FeatureNotEnabledException fneEx)
        {
            this.LogError(fneEx, $"Not enabled on device, {fneEx.Message}.");
        }
        catch (PermissionException)
        {
            this.LogWarning($"User denied.");
        }
        catch (Exception ex)
        {
            this.LogError(ex, $"Error occurs while getting cached location, {ex.Message}");
        }
        return null;
    }

    protected Task<Location?> GetLastLocationAsync()
    {
        if (MainThread.IsMainThread)
        {
            return GetLastLocationAsyncInternal();
        }
        var source = new TaskCompletionSource<Location?>();
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            var location = await GetLastLocationAsyncInternal();
            source.TrySetResult(location);
        });
        return source.Task;
    }

    TaskCompletionSource<Location?>? locationTaskSource;
    CancellationTokenSource? locationCancellationTokenSource;

    async Task<Location?> GetCurrentLocationAsyncInternal()
    {
        if (locationTaskSource == null)
        {
            locationTaskSource = new TaskCompletionSource<Location?>();

            try
            {
                var request = new GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(10));
#if IOS
                request.RequestFullAccuracy = true;
#endif

                locationCancellationTokenSource = new CancellationTokenSource();

                var location = await Geolocation.Default.GetLocationAsync(request, locationCancellationTokenSource.Token);
                locationTaskSource.SetResult(location);
            }
            catch (Exception ex)
            {
                this.LogError(ex, $"Error occurs while getting current location, {ex.Message}");
            }
        }

        return await locationTaskSource.Task;
    }

    protected Task<Location?> GetCurrentLocationAsync()
    {
        if (MainThread.IsMainThread)
        {
            return GetCurrentLocationAsyncInternal();
        }
        var source = new TaskCompletionSource<Location?>();
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            var location = await GetCurrentLocationAsyncInternal();
            source.TrySetResult(location);
        });
        return source.Task;
    }

    protected void CancelRequestLocation()
    {
        if (locationCancellationTokenSource?.IsCancellationRequested == false)
        {
            locationCancellationTokenSource.Cancel();
        }
    }

    async Task<FileResult?> TakePhotoInternal()
    {
        var status = await Permissions.CheckStatusAsync<Permissions.Camera>();

        if (status == PermissionStatus.Denied)
        {
            await this.AlertError(L("needCameraPermission", "Flower Story needs access to the camera to take photos."));
#if IOS
            var settingsUrl = UIKit.UIApplication.OpenSettingsUrlString;
            await Launcher.TryOpenAsync(settingsUrl);
#endif
            return null;
        }

        if (status != PermissionStatus.Granted)
        {
            status = await Permissions.RequestAsync<Permissions.Camera>();
        }

        if (status != PermissionStatus.Granted)
        {
            return null;
        }

        var file = await MediaPicker.Default.CapturePhotoAsync();
        return file;
    }

    protected Task<FileResult?> TakePhoto()
    {
        if (MainThread.IsMainThread)
        {
            return TakePhotoInternal();
        }
        var source = new TaskCompletionSource<FileResult?>();
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            var file = await TakePhotoInternal();
            source.TrySetResult(file);
        });
        return source.Task;
    }
}
