using System;
using System.Globalization;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;

namespace ClaudeDo.Ui.Views.Controls;

public partial class ThemedDatePicker : UserControl
{
    public static readonly StyledProperty<DateTime?> SelectedDateProperty =
        AvaloniaProperty.Register<ThemedDatePicker, DateTime?>(
            nameof(SelectedDate), defaultBindingMode: BindingMode.TwoWay);

    public static readonly StyledProperty<bool> ShowTimeProperty =
        AvaloniaProperty.Register<ThemedDatePicker, bool>(nameof(ShowTime), false);

    public static readonly StyledProperty<bool> IsRangeProperty =
        AvaloniaProperty.Register<ThemedDatePicker, bool>(nameof(IsRange), false);

    public static readonly StyledProperty<DateTime?> StartDateProperty =
        AvaloniaProperty.Register<ThemedDatePicker, DateTime?>(
            nameof(StartDate), defaultBindingMode: BindingMode.TwoWay);

    public static readonly StyledProperty<DateTime?> EndDateProperty =
        AvaloniaProperty.Register<ThemedDatePicker, DateTime?>(
            nameof(EndDate), defaultBindingMode: BindingMode.TwoWay);

    public static readonly StyledProperty<string?> WatermarkProperty =
        AvaloniaProperty.Register<ThemedDatePicker, string?>(nameof(Watermark), "Pick a date");

    public static readonly StyledProperty<string?> DisplayTextProperty =
        AvaloniaProperty.Register<ThemedDatePicker, string?>(nameof(DisplayText));

    public static readonly StyledProperty<IBrush?> DisplayForegroundProperty =
        AvaloniaProperty.Register<ThemedDatePicker, IBrush?>(nameof(DisplayForeground));

    public static readonly StyledProperty<string?> TimeTextProperty =
        AvaloniaProperty.Register<ThemedDatePicker, string?>(
            nameof(TimeText), "09:00",
            defaultBindingMode: BindingMode.TwoWay);

    public DateTime? SelectedDate
    {
        get => GetValue(SelectedDateProperty);
        set => SetValue(SelectedDateProperty, value);
    }

    public bool ShowTime
    {
        get => GetValue(ShowTimeProperty);
        set => SetValue(ShowTimeProperty, value);
    }

    public bool IsRange
    {
        get => GetValue(IsRangeProperty);
        set => SetValue(IsRangeProperty, value);
    }

    public DateTime? StartDate
    {
        get => GetValue(StartDateProperty);
        set => SetValue(StartDateProperty, value);
    }

    public DateTime? EndDate
    {
        get => GetValue(EndDateProperty);
        set => SetValue(EndDateProperty, value);
    }

    public string? Watermark
    {
        get => GetValue(WatermarkProperty);
        set => SetValue(WatermarkProperty, value);
    }

    public string? DisplayText
    {
        get => GetValue(DisplayTextProperty);
        set => SetValue(DisplayTextProperty, value);
    }

    public IBrush? DisplayForeground
    {
        get => GetValue(DisplayForegroundProperty);
        set => SetValue(DisplayForegroundProperty, value);
    }

    public string? TimeText
    {
        get => GetValue(TimeTextProperty);
        set => SetValue(TimeTextProperty, value);
    }

    private static readonly string[] TimeFormats = { @"h\:mm", @"hh\:mm" };

    private DateTime _displayMonth;
    private bool _suppressTimeSync;

    public ThemedDatePicker()
    {
        InitializeComponent();
        _displayMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
        BuildWeekdayHeaders();
        BuildDayGrid();
        UpdateDisplayText();
        UpdateTimeRowVisibility();

        PickerPopup.Opened += OnPopupOpened;
    }

    private void UpdateTimeRowVisibility()
    {
        if (TimeRow is null) return;
        TimeRow.IsVisible = ShowTime && !IsRange;
    }

    private void OnPopupOpened(object? sender, EventArgs e)
    {
        var seed = AnchorDate() ?? DateTime.Today;
        _displayMonth = new DateTime(seed.Year, seed.Month, 1);
        BuildDayGrid();
    }

    private DateTime? AnchorDate() =>
        IsRange ? (StartDate ?? EndDate) : SelectedDate;

    protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
    {
        base.OnPropertyChanged(change);
        if (change.Property == SelectedDateProperty)
        {
            UpdateDisplayText();
            SyncTimeFromSelected();
            BuildDayGrid();
        }
        else if (change.Property == StartDateProperty || change.Property == EndDateProperty)
        {
            UpdateDisplayText();
            BuildDayGrid();
        }
        else if (change.Property == ShowTimeProperty || change.Property == WatermarkProperty
                 || change.Property == IsRangeProperty)
        {
            UpdateDisplayText();
            BuildDayGrid();
            UpdateTimeRowVisibility();
        }
        else if (change.Property == TimeTextProperty)
        {
            ApplyTimeTextToSelected();
        }
    }

    private void UpdateDisplayText()
    {
        if (IsRange)
        {
            var (s, end) = NormalizeRange(StartDate?.Date, EndDate?.Date);
            if (s is null && end is null)
            {
                DisplayText = Watermark ?? "Pick a range";
                DisplayForeground = TryGetBrush("TextDimBrush");
                return;
            }
            if (s is not null && end is null)
            {
                DisplayText = $"{s.Value:MMM d} – select end";
                DisplayForeground = TryGetBrush("TextBrush");
                return;
            }
            // both set
            var sd = s!.Value;
            var ed = end!.Value;
            DisplayText = sd.Year == ed.Year
                ? $"{sd:MMM d} – {ed:MMM d, yyyy}"
                : $"{sd:MMM d, yyyy} – {ed:MMM d, yyyy}";
            DisplayForeground = TryGetBrush("TextBrush");
            return;
        }

        if (SelectedDate is null)
        {
            DisplayText = Watermark ?? "Pick a date";
            DisplayForeground = TryGetBrush("TextDimBrush");
            return;
        }
        var d = SelectedDate.Value;
        DisplayText = ShowTime
            ? d.ToString("MMM d, yyyy · HH:mm", CultureInfo.CurrentCulture)
            : d.ToString("MMM d, yyyy", CultureInfo.CurrentCulture);
        DisplayForeground = TryGetBrush("TextBrush");
    }

    private static (DateTime? Start, DateTime? End) NormalizeRange(DateTime? a, DateTime? b)
    {
        if (a is null && b is null) return (null, null);
        if (a is null) return (b, b);
        if (b is null) return (a, null);
        return a.Value <= b.Value ? (a, b) : (b, a);
    }

    private IBrush? TryGetBrush(string key)
    {
        if (this.TryFindResource(key, out var v) && v is IBrush b) return b;
        return null;
    }

    private void SyncTimeFromSelected()
    {
        if (SelectedDate is null) return;
        _suppressTimeSync = true;
        TimeText = SelectedDate.Value.ToString("HH:mm");
        _suppressTimeSync = false;
    }

    private void ApplyTimeTextToSelected()
    {
        if (_suppressTimeSync || !ShowTime || IsRange || SelectedDate is null) return;
        if (TryParseTime(TimeText, out var t))
        {
            var d = SelectedDate.Value.Date + t;
            if (d != SelectedDate) SelectedDate = d;
        }
    }

    private static bool TryParseTime(string? text, out TimeSpan ts)
    {
        ts = default;
        if (string.IsNullOrWhiteSpace(text)) return false;
        return TimeSpan.TryParseExact(text, TimeFormats, CultureInfo.InvariantCulture, out ts);
    }

    private void BuildWeekdayHeaders()
    {
        if (WeekdayHeaders is null) return;
        WeekdayHeaders.Children.Clear();
        var firstDow = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
        var names = CultureInfo.CurrentCulture.DateTimeFormat.AbbreviatedDayNames;
        for (int i = 0; i < 7; i++)
        {
            var dow = (DayOfWeek)(((int)firstDow + i) % 7);
            var name = names[(int)dow];
            if (name.Length > 3) name = name.Substring(0, 3);
            WeekdayHeaders.Children.Add(new TextBlock { Text = name, Classes = { "weekday" } });
        }
    }

    private void BuildDayGrid()
    {
        if (DayGrid is null || MonthHeader is null) return;
        DayGrid.Children.Clear();
        MonthHeader.Text = _displayMonth.ToString("MMMM yyyy", CultureInfo.CurrentCulture);

        var firstDow = CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek;
        var offset = ((int)_displayMonth.DayOfWeek - (int)firstDow + 7) % 7;
        var start = _displayMonth.AddDays(-offset);

        var today = DateTime.Today;
        var sel = SelectedDate?.Date;
        var (rs, re) = NormalizeRange(StartDate?.Date, EndDate?.Date);
        var rangeFill = TryGetBrush("AccentSoftBrush");

        for (int i = 0; i < 42; i++)
        {
            var day = start.AddDays(i);
            var cell = new Grid();

            if (IsRange && rs.HasValue && re.HasValue && rs.Value != re.Value
                && day >= rs.Value && day <= re.Value)
            {
                Thickness margin;
                if (day == rs.Value)
                    margin = new Thickness(19, 0, 0, 0);
                else if (day == re.Value)
                    margin = new Thickness(0, 0, 19, 0);
                else
                    margin = default;

                cell.Children.Add(new Border
                {
                    Background = rangeFill,
                    Margin = margin,
                    HorizontalAlignment = HorizontalAlignment.Stretch,
                    VerticalAlignment = VerticalAlignment.Stretch
                });
            }

            var btn = new Button
            {
                Content = day.Day.ToString(CultureInfo.CurrentCulture),
                Classes = { "day" },
                Tag = day
            };
            if (day.Month != _displayMonth.Month) btn.Classes.Add("outside");
            if (day == today) btn.Classes.Add("today");

            var isSelected = IsRange
                ? (rs.HasValue && day == rs.Value) || (re.HasValue && day == re.Value)
                : sel.HasValue && day == sel.Value;
            if (isSelected) btn.Classes.Add("selected");

            btn.Click += OnDayClick;
            cell.Children.Add(btn);
            DayGrid.Children.Add(cell);
        }
    }

    private void OnDayClick(object? sender, RoutedEventArgs e)
    {
        if (sender is not Button { Tag: DateTime day }) return;

        if (IsRange)
        {
            // State A: nothing or both set → start a fresh range
            // State B: only start set → complete (or restart) range
            if (StartDate is null || EndDate is not null)
            {
                StartDate = day.Date;
                EndDate = null;
            }
            else
            {
                var s = StartDate.Value.Date;
                if (day.Date < s)
                {
                    StartDate = day.Date;
                    EndDate = s;
                }
                else
                {
                    EndDate = day.Date;
                }
            }
            BuildDayGrid();
            return;
        }

        var time = TimeSpan.Zero;
        if (ShowTime)
        {
            if (TryParseTime(TimeText, out var parsed)) time = parsed;
            else if (SelectedDate is { } cur) time = cur.TimeOfDay;
        }
        else if (SelectedDate is { } cur) time = cur.TimeOfDay;

        SelectedDate = day.Date + time;
        if (!ShowTime) PickerPopup.IsOpen = false;
    }

    private void OnPrevMonthClick(object? sender, RoutedEventArgs e)
    {
        _displayMonth = _displayMonth.AddMonths(-1);
        BuildDayGrid();
    }

    private void OnNextMonthClick(object? sender, RoutedEventArgs e)
    {
        _displayMonth = _displayMonth.AddMonths(1);
        BuildDayGrid();
    }

    private void OnTodayClick(object? sender, RoutedEventArgs e) => SetQuickDate(DateTime.Today);

    private void OnTomorrowClick(object? sender, RoutedEventArgs e) => SetQuickDate(DateTime.Today.AddDays(1));

    private void OnNextMondayClick(object? sender, RoutedEventArgs e)
    {
        var today = DateTime.Today;
        int delta = ((int)DayOfWeek.Monday - (int)today.DayOfWeek + 7) % 7;
        if (delta == 0) delta = 7;
        SetQuickDate(today.AddDays(delta));
    }

    private void OnClearClick(object? sender, RoutedEventArgs e)
    {
        if (IsRange)
        {
            StartDate = null;
            EndDate = null;
        }
        else
        {
            SelectedDate = null;
        }
        PickerPopup.IsOpen = false;
    }

    private void SetQuickDate(DateTime date)
    {
        _displayMonth = new DateTime(date.Year, date.Month, 1);

        if (IsRange)
        {
            StartDate = date.Date;
            EndDate = date.Date;
            BuildDayGrid();
            PickerPopup.IsOpen = false;
            return;
        }

        var time = TimeSpan.Zero;
        if (ShowTime)
        {
            if (!TryParseTime(TimeText, out time))
                time = SelectedDate?.TimeOfDay ?? new TimeSpan(9, 0, 0);
        }
        SelectedDate = date.Date + time;
        BuildDayGrid();
        if (!ShowTime) PickerPopup.IsOpen = false;
    }

    private void OnDoneClick(object? sender, RoutedEventArgs e)
    {
        ApplyTimeTextToSelected();
        PickerPopup.IsOpen = false;
    }
}
