using System.ComponentModel;
using static Blahblah.FlowerApp.Extensions;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace Blahblah.FlowerApp.Controls;

public class TitleLabel : Label { }

public class SecondaryLabel : Label { }

public class IconLabel : Label { }

public class OptionEntry : Entry { }

public class OptionEditor : Editor { }

public class OptionDatePicker : DatePicker { }

public class OptionTimePicker : TimePicker { }

public abstract class OptionCell : ViewCell
{
    public static readonly BindableProperty IconProperty = CreateProperty<ImageSource, OptionCell>(nameof(Icon));
    public static readonly BindableProperty TitleProperty = CreateProperty<string, OptionCell>(nameof(Title));
    public static readonly BindableProperty IsRequiredProperty = CreateProperty<bool, OptionCell>(nameof(IsRequired));

    [TypeConverter(typeof(ImageSourceConverter))]
    public ImageSource Icon
    {
        get => (ImageSource)GetValue(IconProperty);
        set => SetValue(IconProperty, value);
    }
    public string Title
    {
        get => (string)GetValue(TitleProperty);
        set => SetValue(TitleProperty, value);
    }
    public bool IsRequired
    {
        get => (bool)GetValue(IsRequiredProperty);
        set => SetValue(IsRequiredProperty, value);
    }

    protected abstract View Content { get; }

    public OptionCell()
    {
        View = new Grid
        {
            BindingContext = this,
            Padding = new Thickness(20, 0),
            ColumnSpacing = 12,
            ColumnDefinitions =
            {
                new(GridLength.Auto),
                new(new GridLength(.35, GridUnitType.Star)),
                new(new GridLength(.65, GridUnitType.Star))
            },
            RowDefinitions = { new(44) },
            Children =
            {
                new Image
                {
                    WidthRequest = 20,
                    HeightRequest = 20,
                    Aspect = Aspect.AspectFit,
                    VerticalOptions = LayoutOptions.Center
                }
                .Binding(VisualElement.IsVisibleProperty, nameof(Icon), converter: new VisibleIfNotNullConverter())
                .Binding(Image.SourceProperty, nameof(Icon)),

                new Grid
                {
                    ColumnDefinitions =
                    {
                        new(GridLength.Auto),
                        new(GridLength.Star)
                    },
                    Children =
                    {
                        new TitleLabel
                        {
                            LineBreakMode = LineBreakMode.TailTruncation,
                            VerticalOptions = LayoutOptions.Center
                        }
                        .Binding(Label.TextProperty, nameof(Title)),

                        new SecondaryLabel
                        {
                            VerticalOptions = LayoutOptions.Center,
                            Text = "*"
                        }
                        .GridColumn(1)
                        .AppThemeBinding(Label.TextColorProperty, Res.Red100, Res.Red300)
                        .Binding(VisualElement.IsVisibleProperty, nameof(IsRequired)),
                    }
                }
                .GridColumn(1),

                Content.GridColumn(2)
            }
        }
        .AppThemeBinding(VisualElement.BackgroundColorProperty, Colors.White, Res.Gray900);
    }
}

public abstract class OptionVerticalCell : OptionCell
{
    public OptionVerticalCell()
    {
        View = new Grid
        {
            BindingContext = this,
            Padding = new Thickness(20, 0),
            ColumnSpacing = 12,
            ColumnDefinitions =
            {
                new(GridLength.Auto),
                new(GridLength.Star)
            },
            RowDefinitions =
            {
                new(44),
                new(GridLength.Star)
            },
            Children =
            {
                new Image
                {
                    WidthRequest = 20,
                    HeightRequest = 20,
                    Aspect = Aspect.AspectFit,
                    VerticalOptions = LayoutOptions.Center
                }
                .Binding(VisualElement.IsVisibleProperty, nameof(Icon), converter: new VisibleIfNotNullConverter())
                .Binding(Image.SourceProperty, nameof(Icon)),

                new TitleLabel
                {
                    LineBreakMode = LineBreakMode.TailTruncation,
                    VerticalOptions = LayoutOptions.Center
                }
                .GridColumn(1)
                .Binding(Label.TextProperty, nameof(Title)),

                Content.GridRow(1).GridColumn(1)
            }
        }
        .AppThemeBinding(VisualElement.BackgroundColorProperty, Colors.White, Res.Gray900);
    }
}

public class OptionTextCell : OptionCell
{
    public static readonly BindableProperty DetailProperty = CreateProperty<string, OptionTextCell>(nameof(Detail));

    public string Detail
    {
        get => (string)GetValue(DetailProperty);
        set => SetValue(DetailProperty, value);
    }

    protected override View Content => new SecondaryLabel
    {
        HorizontalOptions = LayoutOptions.End,
        VerticalOptions = LayoutOptions.Center
    }
    .Binding(Label.TextProperty, nameof(Detail));
}

public class OptionEntryCell : OptionCell
{
    public static readonly BindableProperty TextProperty = CreateProperty<string, OptionEntryCell>(nameof(Text), defaultBindingMode: BindingMode.TwoWay);
    public static readonly BindableProperty KeyboardProperty = CreateProperty<Keyboard, OptionEntryCell>(nameof(Keyboard), defaultValue: Keyboard.Default);
    public static readonly BindableProperty PlaceholderProperty = CreateProperty<string, OptionEntryCell>(nameof(Placeholder));

    public event EventHandler<FocusEventArgs>? Unfocused;

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }
    public Keyboard Keyboard
    {
        get => (Keyboard)GetValue(KeyboardProperty);
        set => SetValue(KeyboardProperty, value);
    }
    public string Placeholder
    {
        get => (string)GetValue(PlaceholderProperty);
        set => SetValue(PlaceholderProperty, value);
    }

    protected override View Content
    {
        get
        {
            var entry = new OptionEntry()
                .Binding(Entry.TextProperty, nameof(Text))
                .Binding(InputView.KeyboardProperty, nameof(Keyboard))
                .Binding(Entry.PlaceholderProperty, nameof(Placeholder));
            entry.Unfocused += Entry_Unfocused;
            return entry;
        }
    }

    private void Entry_Unfocused(object? sender, FocusEventArgs e)
    {
        Unfocused?.Invoke(this, e);
    }
}

public class OptionEditorCell : OptionVerticalCell
{
    public static readonly BindableProperty TextProperty = CreateProperty<string, OptionEditorCell>(nameof(Text), defaultBindingMode: BindingMode.TwoWay);
    public static readonly BindableProperty KeyboardProperty = CreateProperty<Keyboard, OptionEditorCell>(nameof(Keyboard), defaultValue: Keyboard.Default);
    public static readonly BindableProperty PlaceholderProperty = CreateProperty<string, OptionEditorCell>(nameof(Placeholder));

    public string Text
    {
        get => (string)GetValue(TextProperty);
        set => SetValue(TextProperty, value);
    }
    public Keyboard Keyboard
    {
        get => (Keyboard)GetValue(KeyboardProperty);
        set => SetValue(KeyboardProperty, value);
    }
    public string Placeholder
    {
        get => (string)GetValue(PlaceholderProperty);
        set => SetValue(PlaceholderProperty, value);
    }

    protected override View Content => new OptionEditor()
    .Binding(Editor.TextProperty, nameof(Text))
    .Binding(InputView.KeyboardProperty, nameof(Keyboard))
    .Binding(Editor.PlaceholderProperty, nameof(Placeholder));
}

public class OptionSwitchCell : OptionCell
{
    public static readonly BindableProperty IsToggledProperty = CreateProperty<bool, OptionSwitchCell>(nameof(IsToggled), defaultBindingMode: BindingMode.TwoWay);

    public bool IsToggled
    {
        get => (bool)GetValue(IsToggledProperty);
        set => SetValue(IsToggledProperty, value);
    }

    protected override View Content => new Switch
    {
        HorizontalOptions = LayoutOptions.End,
        VerticalOptions = LayoutOptions.Center
    }
    .Binding(Switch.IsToggledProperty, nameof(IsToggled));
}

public class OptionSelectCell : OptionTextCell
{
    public static readonly BindableProperty CommandProperty = CreateProperty<Command, OptionSelectCell>(nameof(Command));
    public static readonly BindableProperty CommandParameterProperty = CreateProperty<object, OptionSelectCell>(nameof(CommandParameter));

    public Command Command
    {
        get => (Command)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }
    public object CommandParameter
    {
        get => GetValue(CommandParameterProperty);
        set => SetValue(CommandParameterProperty, value);
    }

    public event EventHandler? DetailTapped;

    protected override View Content
    {
        get
        {
            var tap = new TapGestureRecognizer();
            tap.Tapped += OnTapped;

            return new StackLayout
            {
                Orientation = StackOrientation.Horizontal,
                HorizontalOptions = LayoutOptions.End,
                Children =
                {
                    new SecondaryLabel
                    {
                        VerticalOptions = LayoutOptions.Center,
                    }
                    .Binding(Label.TextProperty, nameof(Detail)),

                    new IconLabel
                    {
                        VerticalOptions = LayoutOptions.Center,
                        Margin = new Thickness(6, 0),
                        Text = Res.Right
                    }
                },
                GestureRecognizers = { tap }
            };
        }
    }

    private void OnTapped(object? sender, TappedEventArgs e)
    {
        DetailTapped?.Invoke(this, e);
    }
}

public class OptionDateTimePickerCell : OptionCell
{
    public static readonly BindableProperty DateProperty = CreateProperty<DateTime, OptionDateTimePickerCell>(nameof(Date), defaultBindingMode: BindingMode.TwoWay);
    public static readonly BindableProperty TimeProperty = CreateProperty<TimeSpan, OptionDateTimePickerCell>(nameof(Time), defaultBindingMode: BindingMode.TwoWay);

    public DateTime Date
    {
        get => (DateTime)GetValue(DateProperty);
        set => SetValue(DateProperty, value);
    }
    public TimeSpan Time
    {
        get => (TimeSpan)GetValue(TimeProperty);
        set => SetValue(TimeProperty, value);
    }

    protected override View Content => new Grid
    {
        ColumnDefinitions =
        {
            new(GridLength.Star),
            new(GridLength.Auto),
            new(GridLength.Auto)
        },
        ColumnSpacing = 6,
        Children =
        {
            new OptionDatePicker()
            .Binding(DatePicker.DateProperty, nameof(Date))
            .GridColumn(1),

            new OptionTimePicker()
            .Binding(TimePicker.TimeProperty, nameof(Time))
            .GridColumn(2)
        }
    };
}
