@page "/Calendar"
@using salesbook.Shared.Components.Layout
@using salesbook.Shared.Components.Layout.Spinner
@using salesbook.Shared.Components.SingleElements
@using salesbook.Shared.Components.SingleElements.BottomSheet
@using salesbook.Shared.Core.Dto.Activity
@using salesbook.Shared.Core.Dto.PageState
@using salesbook.Shared.Core.Interface
@using salesbook.Shared.Core.Messages.Activity.New
@inject IManageDataService ManageData
@inject IJSRuntime JS
@inject NewActivityService NewActivity
@inject UserListState UserState

<HeaderLayout Title="@_headerTitle"
              ShowFilter="true"
              ShowCalendarToggle="true"
              OnFilterToggle="ToggleFilter"
              OnCalendarToggle="ToggleExpanded"/>

<div @ref="_weekSliderRef" class="container week-slider @(Expanded ? "expanded" : "") @(SliderAnimation)">
    @if (Expanded)
    {
        <!-- Vista mensile -->
        @foreach (var nomeGiorno in GiorniSettimana)
        {
            <div class="week-day">
                <div>@nomeGiorno</div>
            </div>
        }

        @foreach (var unused in Enumerable.Range(0, StartOffset))
        {
            <div class="day" style="visibility: hidden"></div>
        }

        @if (_isInitialized && _monthDaysData.Length > 0)
        {
            @for (var d = 1; d <= DaysInMonth; d++)
            {
                var day = new DateTime(CurrentMonth.Year, CurrentMonth.Month, d);
                var dayData = _monthDaysData[d - 1];

                <div class="day @dayData.CssClass"
                     @onclick="() => SelezionaDataDalMese(day)">
                    <div>@d</div>
                    @if (dayData.HasEvents)
                    {
                        <div class="event-dot-container" style="margin-top: 2px;">
                            @foreach (var cat in dayData.EventCategories)
                            {
                                <div class="event-dot @cat.CssClass" title="@cat.Title"></div>
                            }
                        </div>
                    }
                </div>
            }
        }
        else
        {
            @* Fallback rendering per prima inizializzazione *@
            @for (var d = 1; d <= DaysInMonth; d++)
            {
                var day = new DateTime(CurrentMonth.Year, CurrentMonth.Month, d);
                var isSelected = IsSameDay(day, SelectedDate);
                var isToday = IsSameDay(day, DateTime.Today);
                var events = GetEventsForDay(day);

                <div class="day @(isSelected ? "selected" : (isToday ? "today" : ""))"
                     @onclick="() => SelezionaDataDalMese(day)">
                    <div>@d</div>
                    @if (events.Any())
                    {
                        <div class="event-dot-container" style="margin-top: 2px;">
                            @foreach (var cat in events.Select(x => x.Category).Distinct())
                            {
                                <div class="event-dot @cat.ConvertToHumanReadable()" title="@cat.ConvertToHumanReadable()"></div>
                            }
                        </div>
                    }
                </div>
            }
        }

        @foreach (var unused in Enumerable.Range(0, EndOffset))
        {
            <div class="day" style="visibility: hidden"></div>
        }
    }
    else
    {
        <!-- Vista settimanale -->
        @if (_isInitialized && _weekDaysData.Length == 7 && _weekDaysData[0].Date != default)
        {
            @for (int i = 0; i < 7; i++)
            {
                var dayData = _weekDaysData[i];
                var day = dayData.Date;

                <div class="week-day">
                    <div>@dayData.DayName</div>
                    <div class="day @dayData.CssClass"
                         @onclick="() => SelezionaData(day)">
                        <div>@day.Day</div>
                        @if (dayData.HasEvents)
                        {
                            <div class="event-dot-container" style="margin-top: 2px;">
                                @foreach (var cat in dayData.EventCategories)
                                {
                                    <div class="event-dot @cat.CssClass" title="@cat.Title"></div>
                                }
                            </div>
                        }
                    </div>
                </div>
            }
        }
        else
        {
            var start = GetStartOfWeek(SelectedDate);
            var culture = new System.Globalization.CultureInfo("it-IT");


            for (var i = 0; i < 7; i++)
            {
                var day = start.AddDays(i);
                var isSelected = IsSameDay(day, SelectedDate);
                var isToday = IsSameDay(day, DateTime.Today);
                var events = GetEventsForDay(day);

                <div class="week-day">
                    <div>@day.ToString("ddd", culture)</div>
                    <div class="day @(isSelected ? "selected" : (isToday ? "today" : ""))"
                         @onclick="() => SelezionaData(day)"
                         aria-label="@day.ToString("dddd d MMMM", culture)">
                        <div>@day.Day</div>
                        @if (events.Any())
                        {
                            <div class="event-dot-container" style="margin-top: 2px;">
                                @foreach (var cat in events.Select(x => x.Category).Distinct())
                                {
                                    <div class="event-dot @cat.ConvertToHumanReadable()" title="@cat.ConvertToHumanReadable()"></div>
                                }
                            </div>
                        }
                    </div>
                </div>
            }
        }
    }
</div>

<div class="container appointments">
    @if (IsLoading)
    {
        <SpinnerLayout FullScreen="false"/>
    }
    else if (FilteredActivities is { Count: > 0 })
    {
        <Virtualize Items="FilteredActivities" Context="activity">
            <ActivityCard Activity="activity" ActivityChanged="OnActivityChanged" ActivityDeleted="OnActivityDeleted" />
        </Virtualize>
    }
    else
    {
        <NoDataAvailable Text="Nessuna attività trovata" ImageSource="_content/salesbook.Shared/images/undraw_file-search_cbur.svg"/>
    }
</div>

<FilterActivity @bind-IsSheetVisible="OpenFilter" @bind-Filter="Filter" @bind-Filter:after="ApplyFilter"/>

@code {
    // Modelli per ottimizzazione rendering
    private record DayData(DateTime Date, string CssClass, bool HasEvents, CategoryData[] EventCategories, string DayName = "");

    private record CategoryData(string CssClass, string Title);

    // Cache per rendering
    private DayData[] _monthDaysData = [];
    private readonly DayData[] _weekDaysData = new DayData[7];
    private string _headerTitle = string.Empty;
    private readonly Dictionary<DateTime, List<ActivityDTO>> _eventsCache = new();
    private readonly Dictionary<DateTime, CategoryData[]> _categoriesCache = new();
    private bool _isInitialized;

    // Stato UI
    private bool Expanded { get; set; }
    private string SliderAnimation { get; set; } = string.Empty;
    private ElementReference _weekSliderRef;
    private DotNetObjectReference<Calendar>? _dotNetHelper;

    // Stato calendario
    private DateTime SelectedDate { get; set; } = DateTime.Today;
    private DateTime _internalMonth = DateTime.Today;
    private DateTime CurrentMonth => new(_internalMonth.Year, _internalMonth.Month, 1);

    // Stato attività
    private List<ActivityDTO> MonthActivities { get; set; } = [];
    private List<ActivityDTO> FilteredActivities { get; set; } = [];
    private bool IsLoading { get; set; } = true;

    // Supporto rendering mese
    private static readonly string[] GiorniSettimana = ["Lu", "Ma", "Me", "Gi", "Ve", "Sa", "Do"];
    private int DaysInMonth => DateTime.DaysInMonth(CurrentMonth.Year, CurrentMonth.Month);
    private int StartOffset => (int)CurrentMonth.DayOfWeek == 0 ? 6 : (int)CurrentMonth.DayOfWeek - 1;

    //Filtri
    private bool OpenFilter { get; set; }
    private FilterActivityDTO Filter { get; set; } = new();

    private int EndOffset
    {
        get
        {
            var totalCells = (int)Math.Ceiling((DaysInMonth + StartOffset) / 7.0) * 7;
            return totalCells - (DaysInMonth + StartOffset);
        }
    }

    protected override void OnInitialized()
    {
        PrepareRenderingData();

        NewActivity.OnActivityCreated += async activityId => await OnActivityCreated(activityId);
        UserState.OnUsersLoaded += async () => await InvokeAsync(LoadData);
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && UserState.IsLoaded)
        {
            await LoadData();
        }
    }

    private async Task LoadData()
    {
        if (!_isInitialized)
        {
            Filter.User = new HashSet<string> { UserSession.User.Username };

            _dotNetHelper = DotNetObjectReference.Create(this);
            await JS.InvokeVoidAsync("calendarSwipe.register", _weekSliderRef, _dotNetHelper);
            _internalMonth = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
            await LoadMonthData();

            _isInitialized = true;
            ApplyFilter();
            StateHasChanged();
        }
    }

    // Metodo per preparare i dati di rendering una sola volta
    private void PrepareRenderingData()
    {
        PrepareHeaderTitle();
        PrepareEventsCache();

        if (Expanded)
        {
            PrepareMonthDaysData();
        }
        else
        {
            PrepareWeekDaysData();
        }
    }

    private void PrepareHeaderTitle()
    {
        _headerTitle = CurrentMonth.ToString("MMMM yyyy", new System.Globalization.CultureInfo("it-IT")).FirstCharToUpper();
    }

    private void PrepareEventsCache()
    {
        _eventsCache.Clear();
        _categoriesCache.Clear();

        // Raggruppa le attività per data
        var activitiesByDate = MonthActivities
            .GroupBy(x => (x.EffectiveTime ?? x.EstimatedTime!).Value.Date)
            .ToDictionary(g => g.Key, g => g.ToList());

        foreach (var (date, activities) in activitiesByDate)
        {
            _eventsCache[date] = activities;

            // Pre-calcola le categorie per ogni giorno
            var categories = activities
                .Select(x => x.Category)
                .Distinct()
                .Select(cat => new CategoryData(cat.ConvertToHumanReadable(), cat.ConvertToHumanReadable()))
                .ToArray();

            _categoriesCache[date] = categories;
        }
    }

    private void PrepareMonthDaysData()
    {
        _monthDaysData = new DayData[DaysInMonth];
        var today = DateTime.Today;

        for (var d = 1; d <= DaysInMonth; d++)
        {
            var day = new DateTime(CurrentMonth.Year, CurrentMonth.Month, d);
            var isSelected = day.Date == SelectedDate.Date;
            var isToday = day.Date == today;

            var cssClass = isSelected ? "selected" : (isToday ? "today" : "");
            var hasEvents = _eventsCache.ContainsKey(day.Date);
            var eventCategories = hasEvents ? GetFilteredCategoriesForDay(day.Date) : [];

            _monthDaysData[d - 1] = new DayData(day, cssClass, eventCategories.Length > 0, eventCategories);
        }
    }

    private void PrepareWeekDaysData()
    {
        var start = GetStartOfWeek(SelectedDate);
        var today = DateTime.Today;
        var culture = new System.Globalization.CultureInfo("it-IT");

        for (var i = 0; i < 7; i++)
        {
            var day = start.AddDays(i);
            var isSelected = day.Date == SelectedDate.Date;
            var isToday = day.Date == today;

            var cssClass = isSelected ? "selected" : (isToday ? "today" : "");
            var dayName = day.ToString("ddd", culture);
            var hasEvents = _eventsCache.ContainsKey(day.Date);
            var eventCategories = hasEvents ? GetFilteredCategoriesForDay(day.Date) : [];

            _weekDaysData[i] = new DayData(day, cssClass, eventCategories.Length > 0, eventCategories, dayName);
        }
    }

    private CategoryData[] GetFilteredCategoriesForDay(DateTime date)
    {
        if (!_categoriesCache.TryGetValue(date, out var categories))
            return [];

        if (Filter.ClearFilter)
            return categories;

        // Applica i filtri alle categorie
        var filteredActivities = GetFilteredActivitiesForDay(date);
        if (!filteredActivities.Any())
            return [];

        return filteredActivities
            .Select(x => x.Category)
            .Distinct()
            .Select(cat => new CategoryData(cat.ConvertToHumanReadable(), cat.ConvertToHumanReadable()))
            .ToArray();
    }

    private List<ActivityDTO> GetFilteredActivitiesForDay(DateTime date)
    {
        if (!_eventsCache.TryGetValue(date, out var activities))
            return [];

        if (Filter.ClearFilter)
            return activities;

        var filteredActivity = activities.AsQueryable();

        filteredActivity = filteredActivity
            .Where(x => Filter.Text.IsNullOrEmpty() || (x.ActivityDescription != null && x.ActivityDescription.ContainsIgnoreCase(Filter.Text!)));

        filteredActivity = filteredActivity
            .Where(x => Filter.Type.IsNullOrEmpty() || (x.ActivityTypeId != null && x.ActivityTypeId.Equals(Filter.Type)));

        filteredActivity = filteredActivity
            .Where(x => Filter.Result.IsNullOrEmpty() || (x.ActivityResultId != null && x.ActivityResultId.Equals(Filter.Result)));

        filteredActivity = filteredActivity
            .Where(x => Filter.User.IsNullOrEmpty() || (x.UserName != null && Filter.User!.Contains(x.UserName)));

        filteredActivity = filteredActivity
            .Where(x => Filter.Category == null || x.Category.Equals(Filter.Category));

        return filteredActivity
            .OrderBy(x => x.EffectiveTime ?? x.EstimatedTime)
            .ToList();
    }

    [JSInvokable]
    public async Task OnSwipeLeft()
    {
        await CambiaPeriodo(1);
        PrepareRenderingData();
        StateHasChanged();
        if (Expanded)
        {
            await LoadMonthData();
        }
    }

    [JSInvokable]
    public async Task OnSwipeRight()
    {
        await CambiaPeriodo(-1);
        PrepareRenderingData();
        StateHasChanged();
        if (Expanded)
        {
            await LoadMonthData();
        }
    }

    [JSInvokable]
    public async Task OnSwipeDown()
    {
        if (!Expanded)
            ToggleExpanded();
    }

    [JSInvokable]
    public async Task OnSwipeUp()
    {
        if (Expanded)
            ToggleExpanded();
    }

    // Cambio periodo mese/settimana
    private async Task CambiaPeriodo(int direzione)
    {
        if (Expanded)
        {
            var y = CurrentMonth.Year;
            var m = CurrentMonth.Month + direzione;
            if (m < 1)
            {
                y--;
                m = 12;
            }

            if (m > 12)
            {
                y++;
                m = 1;
            }

            _internalMonth = new DateTime(y, m, 1);
        }
        else
        {
            await SelezionaData(SelectedDate.AddDays(7 * direzione));
            _internalMonth = new DateTime(SelectedDate.Year, SelectedDate.Month, 1);
        }
    }

    // Cambio modalità
    private void ToggleExpanded()
    {
        if (Expanded)
        {
            SliderAnimation = "collapse-animation";
            Expanded = false;
        }
        else
        {
            Expanded = true;
            SliderAnimation = "expand-animation";
        }

        PrepareRenderingData();
        StateHasChanged();

        SliderAnimation = "";
        StateHasChanged();
    }

    // Caricamento attività al cambio mese
    private async Task LoadMonthData()
    {
        IsLoading = true;
        StateHasChanged();

        var start = CurrentMonth;
        var end = start.AddDays(DaysInMonth - 1);
        var activities = await ManageData.GetActivity(new WhereCondActivity{Start = start, End = end});
        MonthActivities = activities.OrderBy(x => x.EffectiveTime ?? x.EstimatedTime).ToList();

        PrepareRenderingData();
        IsLoading = false;
        StateHasChanged();
    }

    // Selezione giorno in settimana
    private async Task SelezionaData(DateTime day)
    {
        if (IsLoading) return;
        
        SelectedDate = day;

        var cacheInternalMonth = _internalMonth;
        _internalMonth = new DateTime(day.Year, day.Month, 1);

        if (cacheInternalMonth != _internalMonth)
        {
            await LoadMonthData();
        }
        else
        {
            PrepareRenderingData();
        }

        ApplyFilter();
        StateHasChanged();
    }

    // Selezione giorno dal mese (chiude la vista mese!)
    private async Task SelezionaDataDalMese(DateTime day)
    {
        if (IsLoading) return;
        
        SelectedDate = day;
        SliderAnimation = "collapse-animation";
        Expanded = false;
        _internalMonth = new DateTime(day.Year, day.Month, 1);

        PrepareRenderingData();
        ApplyFilter();
        StateHasChanged();

        SliderAnimation = "";
        StateHasChanged();
    }

    // Utility
    private static bool IsSameDay(DateTime d1, DateTime d2) => d1.Date == d2.Date;

    private static DateTime GetStartOfWeek(DateTime date)
    {
        var day = date.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)date.DayOfWeek;
        return date.AddDays(-day + 1).Date;
    }

    private List<ActivityDTO> GetEventsForDay(DateTime day)
        => _eventsCache.TryGetValue(day.Date, out var events) ? events : [];

    public void Dispose()
    {
        _dotNetHelper?.Dispose();
    }

    private async Task OnActivityDeleted(ActivityDTO activity)
    {
        IsLoading = true;

        await ManageData.DeleteActivity(activity);

        var indexActivity = MonthActivities?.FindIndex(x => x.ActivityId!.Equals(activity.ActivityId));

        if (indexActivity != null)
        {
            MonthActivities?.RemoveAt(indexActivity.Value);
            PrepareRenderingData();

            ApplyFilter();
        }

        IsLoading = false;
    }

    private async Task OnActivityCreated(string activityId)
    {
        IsLoading = true;

        var activity = (await ManageData.GetActivity(new WhereCondActivity {ActivityId = activityId}, true)).LastOrDefault();

        if (activity == null)
        {
            IsLoading = false;
            return;
        }

        var date = activity.EffectiveTime ?? activity.EstimatedTime;

        if (CurrentMonth.Month != date!.Value.Month)
        {
            IsLoading = false;
            return;
        }

        MonthActivities.Add(activity);
        PrepareRenderingData();
        IsLoading = false;

        ApplyFilter();
    }

    private async Task OnActivityChanged(string activityId)
    {
        var newActivity = await ManageData.GetActivity(new WhereCondActivity { ActivityId = activityId }, true);
        var indexActivity = MonthActivities?.FindIndex(x => x.ActivityId.Equals(activityId));

        if (indexActivity != null && !newActivity.IsNullOrEmpty())
        {
            MonthActivities![indexActivity.Value] = newActivity[0];
            PrepareRenderingData(); // Ricalcola i dati di rendering
            ApplyFilter();
        }
    }

    private void ToggleFilter()
    {
        OpenFilter = !OpenFilter;
        StateHasChanged();
    }

    private void ApplyFilter()
    {
        FilteredActivities = GetFilteredActivitiesForDay(SelectedDate);

        // Aggiorna i dati di rendering se il filtro è cambiato
        if (Expanded)
        {
            PrepareMonthDaysData();
        }
        else
        {
            PrepareWeekDaysData();
        }

        StateHasChanged();
    }

    // Metodo ottimizzato per il rendering dei filtri
    private List<ActivityDTO> ReturnFilteredActivity(DateTime day)
    {
        return GetFilteredActivitiesForDay(day);
    }

}