using Blahblah.FlowerApp.Data;
using Microsoft.Extensions.Logging;
using System.Net.Http.Json;

namespace Blahblah.FlowerApp;

internal sealed class Extensions
{
    public static string L(string key, string defaultValue = "")
    {
        return LocalizationResource.GetText(key, defaultValue);
    }

    public static BindableProperty CreateProperty<T, V>(string propertyName, T? defaultValue = default, BindingMode defaultBindingMode = BindingMode.OneWay, BindableProperty.BindingPropertyChangedDelegate? propertyChanged = null)
    {
        return BindableProperty.Create(propertyName, typeof(T), typeof(V), defaultValue, defaultBindingMode, propertyChanged: propertyChanged);
    }

    public static async Task<T?> FetchAsync<T>(string url, CancellationToken cancellation = default)
    {
        using var client = new HttpClient();
        var authorization = Constants.Authorization;
        if (authorization != null)
        {
            client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorization);
        }
        return await client.GetFromJsonAsync<T>($"{Constants.BaseUrl}/{url}", cancellation);
    }

    public static async Task<R?> PostAsync<T, R>(string url, T data, CancellationToken cancellation = default)
    {
        using var client = new HttpClient();
        var authorization = Constants.Authorization;
        if (authorization != null)
        {
            client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorization);
        }
        using var response = await client.PostAsJsonAsync($"{Constants.BaseUrl}/{url}", data, cancellation);
        if (response != null)
        {
            response.EnsureSuccessStatusCode();
            var content = response.Content;
            var result = await content.ReadFromJsonAsync<R>(cancellationToken: cancellation);
            return result;
        }
        return default;
    }

    public static async Task<R?> UploadAsync<R>(string url, MultipartFormDataContent data, CancellationToken cancellation = default)
    {
        using var client = new HttpClient();
        var authorization = Constants.Authorization;
        if (authorization != null)
        {
            client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authorization);
        }
        using var response = await client.PostAsJsonAsync($"{Constants.BaseUrl}/{url}", data, cancellation);
        if (response != null)
        {
            response.EnsureSuccessStatusCode();
            var content = response.Content;
            var result = await content.ReadFromJsonAsync<R>(cancellationToken: cancellation);
            return result;
        }
        return default;
    }

    public static async Task<string> CacheFileAsync(FileResult file)
    {
        string cache = Path.Combine(FileSystem.CacheDirectory, file.FileName);

        using Stream source = await file.OpenReadAsync();
        using FileStream fs = File.OpenWrite(cache);
        await source.CopyToAsync(fs);

        return cache;
    }
}

internal static class LoggerExtension
{
    const LogLevel MinimumLogLevel =
#if DEBUG
        LogLevel.Information;
#else
        LogLevel.Warning;
#endif

    public static void LogInformation(this ILoggerContent content, string message)
    {
        Log(content, LogLevel.Information, null, message);
    }

    public static void LogWarning(this ILoggerContent content, string message)
    {
        Log(content, LogLevel.Warning, null, message);
    }

    public static void LogError(this ILoggerContent content, Exception? exception, string message)
    {
        Log(content, LogLevel.Error, exception, message);
    }

    private static void Log(ILoggerContent content, LogLevel level, Exception? exception, string message)
    {
        if (content?.Logger is ILogger logger)
        {
            logger.Log(level, exception, "[{time:MM/dd HH:mm:ss}] - {message}", DateTime.UtcNow, message);

            if (level >= MinimumLogLevel && content.Database is FlowerDatabase database)
            {
                _ = database.AddLog(new Data.Model.LogItem
                {
                    OwnerId = AppResources.User.Id,
                    LogType = level.ToString(),
                    LogUnixTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                    Category = "logger",
                    ClientAgent = Constants.UserAgent,
                    Message = message,
                    Description = exception?.ToString(),
                    Source = exception?.Source
                });
            }
        }
    }
}

internal static class PageExtension
{
    public static T Binding<T>(this T obj, BindableProperty property, string path, BindingMode mode = BindingMode.Default, IValueConverter? converter = null) where T : BindableObject
    {
        obj.SetBinding(property, path, mode, converter);
        return obj;
    }

    public static T AppThemeBinding<T>(this T obj, BindableProperty property, Color light, Color dark) where T : BindableObject
    {
        obj.SetAppThemeColor(property, light, dark);
        return obj;
    }

    public static T GridColumn<T>(this T view, int column) where T : BindableObject
    {
        Grid.SetColumn(view, column);
        return view;
    }

    public static T GridRow<T>(this T view, int row) where T : BindableObject
    {
        Grid.SetRow(view, row);
        return view;
    }

    public static T GridColumnSpan<T>(this T view, int columnSpan) where T : BindableObject
    {
        Grid.SetColumnSpan(view, columnSpan);
        return view;
    }

    public static T GridRowSpan<T>(this T view, int rowSpan) where T : BindableObject
    {
        Grid.SetRowSpan(view, rowSpan);
        return view;
    }

    public static Task AlertError(this ContentPage page, string error)
    {
        return Alert(page, LocalizationResource.GetText("error", "Error"), error);
    }

    public static Task Alert(this ContentPage page, string title, string message, string? cancel = null)
    {
        cancel ??= LocalizationResource.GetText("ok", "Ok");

        if (MainThread.IsMainThread)
        {
            return page.DisplayAlert(title, message, cancel);
        }
        var taskSource = new TaskCompletionSource();
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            await page.DisplayAlert(title, message, cancel);
            taskSource.TrySetResult();
        });
        return taskSource.Task;
    }

    public static Task<bool> Confirm(this ContentPage page, string title, string question, string? accept = null, string? cancel = null)
    {
        accept ??= LocalizationResource.GetText("yes", "Yes");
        cancel ??= LocalizationResource.GetText("no", "No");

        if (MainThread.IsMainThread)
        {
            return page.DisplayAlert(title, question, accept, cancel);
        }
        var taskSource = new TaskCompletionSource<bool>();
        MainThread.BeginInvokeOnMainThread(async () =>
        {
            var result = await page.DisplayAlert(title, question, accept, cancel);
            taskSource.TrySetResult(result);
        });
        return taskSource.Task;
    }
}

internal static class Res
{
    public const string Filter = "\ue17c";
    public const string Camera = "\uf030";
    public const string Image = "\uf03e";
    public const string Heart = "\uf004";
    public const string Right = "\uf105";
    public const string XMarkLarge = "\ue59b";
    public const string Gear = "\uf013";
    public const string List = "\uf0ae";
    public const string Flag = "\uf2b4";
    public const string Check = "\uf00c";

    public static readonly Color Gray900 = Color.FromRgb(0x21, 0x21, 0x21);
    public static readonly Color Red100 = Color.FromRgb(0xF4, 0x43, 0x36);
    public static readonly Color Red300 = Color.FromRgb(0xFF, 0xCD, 0xD2);
}