@page "/grid"
@page "/grid/{YearMonth}"
@attribute [Authorize]
@rendermode InteractiveServer

@inject IWorkDayService WorkDayService
@inject IItalianFestivitySource FestivitySource
@inject NavigationManager Navigation

<PageTitle>Grid View</PageTitle>

<h1>Grid View</h1>

<div class="d-flex align-items-center gap-2 mb-3">
    <button class="btn btn-outline-secondary btn-sm" @onclick="PreviousMonth">&laquo; Prev</button>
    <h2 class="h5 mb-0">@currentDate.ToString("MMMM yyyy")</h2>
    <button class="btn btn-outline-secondary btn-sm" @onclick="NextMonth">Next &raquo;</button>
</div>

@if (loading)
{
    <p><em>Loading...</em></p>
}
else
{
    <div class="table-responsive">
        <table class="table table-sm table-bordered align-middle grid-view-table">
            <thead class="table-dark">
                <tr>
                    <th>Date</th>
                    <th>Day</th>
                    <th>Work Units</th>
                    <th>Calendar Events</th>
                    <th class="text-end">Counted</th>
                    <th class="text-end">Preview</th>
                    <th class="text-end">Off</th>
                    <th class="text-end">Gross €</th>
                    <th class="text-end">Net €</th>
                    <th></th>
                </tr>
            </thead>
            <tbody>
                @foreach (var row in calendarDays)
                {
                    <tr class="@GetRowClass(row)">
                        <td>@row.Date.ToString("dd")</td>
                        <td>@row.Date.ToString("ddd")</td>
                        <td>
                            @if (row.Entry?.WorkUnits.Count > 0)
                            {
                                @foreach (var unit in row.Entry.WorkUnits)
                                {
                                    <div class="small mb-1">@unit.Label: @FormatTimeRange(unit.StartTime, unit.EndTime) (@FormatHours(unit.ManualWorkedHours)@(unit.IsPreview ? ", preview" : ""))</div>
                                }
                            }
                            else
                            {
                                <span class="text-muted">—</span>
                            }
                        </td>
                        <td>
                            @if (row.Entry?.CalendarEvents.Count > 0)
                            {
                                @foreach (var calendarEvent in row.Entry.CalendarEvents)
                                {
                                    <div class="small mb-1">@CalendarEventFormatter.GetEventTypeName(calendarEvent.EventType): @CalendarEventFormatter.GetDisplayDescription(calendarEvent)</div>
                                }
                            }
                            else
                            {
                                <span class="text-muted">—</span>
                            }
                        </td>
                        <td class="text-end">@FormatHours(GetCountedHours(row))</td>
                        <td class="text-end">@FormatHours(GetPreviewHours(row))</td>
                        <td class="text-end">@FormatHours(GetHoursOff(row))</td>
                        <td class="text-end">@GetGrossIncome(row).ToString("N2")</td>
                        <td class="text-end">@GetNetIncome(row).ToString("N2")</td>
                        <td>
                            <div class="d-flex gap-2">
                                <a href="work-unit/@row.Date.ToString("yyyy-MM-dd")" class="btn btn-sm btn-outline-primary">Unit</a>
                                <a href="calendar-event/@row.Date.ToString("yyyy-MM-dd")" class="btn btn-sm btn-outline-secondary">Event</a>
                            </div>
                        </td>
                    </tr>
                }
            </tbody>
        </table>
    </div>
}

@code {
    [Parameter] public string? YearMonth { get; set; }

    private DateOnly currentDate;
    private bool loading = true;
    private List<CalendarDayRow> calendarDays = [];
    private IReadOnlyCollection<DateOnly> festivities = [];

    protected override async Task OnInitializedAsync()
    {
        if (!string.IsNullOrEmpty(YearMonth) && DateTime.TryParseExact(YearMonth, "yyyy-MM", null, System.Globalization.DateTimeStyles.None, out var parsed))
        {
            currentDate = new DateOnly(parsed.Year, parsed.Month, 1);
        }
        else
        {
            currentDate = new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1);
        }

        await LoadMonth();
    }

    private async Task LoadMonth()
    {
        loading = true;
        festivities = FestivitySource.GetFestivities(currentDate.Year);

        var from = currentDate;
        var to = currentDate.AddMonths(1).AddDays(-1);
        var entries = await WorkDayService.GetRangeAsync(from, to);
        var lookup = entries.ToDictionary(e => e.Date);

        calendarDays = [];
        for (var d = from; d <= to; d = d.AddDays(1))
        {
            calendarDays.Add(new CalendarDayRow
            {
                Date = d,
                IsWeekend = d.DayOfWeek is DayOfWeek.Saturday or DayOfWeek.Sunday,
                IsFestivity = festivities.Contains(d),
                Entry = lookup.GetValueOrDefault(d)
            });
        }

        loading = false;
    }

    private async Task PreviousMonth()
    {
        currentDate = currentDate.AddMonths(-1);
        await LoadMonth();
    }

    private async Task NextMonth()
    {
        currentDate = currentDate.AddMonths(1);
        await LoadMonth();
    }

    private string GetRowClass(CalendarDayRow row)
    {
        if (row.IsWeekend || row.IsFestivity) return "grid-row-weekend";
        if (row.Entry is null) return string.Empty;
        if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Holiday))
        {
            return "grid-row-holiday";
        }

        if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Closure))
        {
            return "grid-row-closure";
        }

        if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.Illness))
        {
            return "grid-row-illness";
        }

        if (row.Entry.CalendarEvents.Any(entry => entry.EventType == CalendarEventType.DayOff))
        {
            return "grid-row-dayoff";
        }

        if (row.Entry.WorkUnits.Any(entry => entry.Location == WorkUnitLocation.Home))
        {
            return "grid-row-home";
        }

        return string.Empty;
    }

    private static decimal GetCountedHours(CalendarDayRow row)
    {
        return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.ManualWorkedHours) ?? 0m;
    }

    private static decimal GetPreviewHours(CalendarDayRow row)
    {
        return row.Entry?.WorkUnits.Where(unit => unit.IsPreview).Sum(unit => unit.ManualWorkedHours) ?? 0m;
    }

    private static decimal GetHoursOff(CalendarDayRow row)
    {
        if (row.Entry is null || row.Entry.WorkUnits.Count == 0)
        {
            return 0m;
        }

        var countedUnits = row.Entry.WorkUnits.Where(unit => !unit.IsPreview).ToList();
        if (countedUnits.Count == 0)
        {
            return 0m;
        }

        var standardHours = countedUnits[0].CoeffSnapshot.StandardWorkHoursPerDay;
        return Math.Max(0m, standardHours - countedUnits.Sum(unit => unit.ManualWorkedHours));
    }

    private static decimal GetGrossIncome(CalendarDayRow row)
    {
        return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.GrossIncome) ?? 0m;
    }

    private static decimal GetNetIncome(CalendarDayRow row)
    {
        return row.Entry?.WorkUnits.Where(unit => !unit.IsPreview).Sum(unit => unit.NetIncome) ?? 0m;
    }

    private static string FormatTimeRange(TimeOnly? startTime, TimeOnly? endTime)
    {
        if (startTime.HasValue && endTime.HasValue)
        {
            return $"{startTime:HH:mm}-{endTime:HH:mm}";
        }

        if (startTime.HasValue)
        {
            return startTime.Value.ToString("HH:mm");
        }

        return "No time range";
    }

    private static string FormatHours(decimal value)
    {
        return DurationFormatter.FormatHours(value);
    }

    private sealed class CalendarDayRow
    {
        public DateOnly Date { get; set; }
        public bool IsWeekend { get; set; }
        public bool IsFestivity { get; set; }
        public WorkDayDocument? Entry { get; set; }
    }
}
