@inject IWorkDayService WorkDayService
@inject IAppSettingsService AppSettingsService
@inject IJSRuntime JS

<div class="calendar-modal-backdrop">
    <div class="calendar-modal-shell">
        <div class="calendar-modal-dialog" role="dialog" aria-modal="true" aria-labelledby="work-unit-modal-title" @onclick:stopPropagation="true">
            <div class="calendar-modal-header">
                <div>
                    <h2 id="work-unit-modal-title" class="h5 mb-1">@(isExistingUnit ? "Edit Work Unit" : "New Work Unit")</h2>
                    <div class="small text-muted">@FormatDisplayDate(selectedDate)</div>
                </div>
                <button type="button" class="btn-close" aria-label="Close" @onclick="CloseAsync"></button>
            </div>

            <div class="calendar-modal-body">
                @if (!loaded)
                {
                    <p class="mb-0"><em>Loading...</em></p>
                }
                else
                {
                    <div class="row g-3">
                        <div class="col-12 col-md-6 col-lg-4">
                            <label class="form-label">Date</label>
                            <LocalizedDateInput InputId="work-unit-modal-date" TestId="work-unit-modal-date" Value="@selectedDate" ValueChanged="OnDateChangedAsync" Disabled="@isExistingUnit" />
                        </div>

                        <div class="col-12 col-md-6 col-lg-4">
                            <label class="form-label">Label</label>
                            <input class="form-control" @bind="label" maxlength="40" />
                        </div>

                        <div class="col-12 col-md-6 col-lg-4">
                            <label class="form-label">Location</label>
                            <select class="form-select" @bind="location">
                                @foreach (var item in Enum.GetValues<WorkUnitLocation>())
                                {
                                    <option value="@item">@item</option>
                                }
                            </select>
                        </div>

                        <div class="col-12 col-md-6 col-lg-4">
                            <label class="form-label">Start Time</label>
                            <input type="time" lang="it-IT" class="form-control" value="@startTimeStr" @onchange="OnStartTimeChanged" />
                        </div>

                        <div class="col-12 col-md-6 col-lg-4">
                            <label class="form-label">End Time</label>
                            <input type="time" lang="it-IT" class="form-control" value="@endTimeStr" @onchange="OnEndTimeChanged" />
                        </div>

                        <div class="col-12 col-md-6 col-lg-4">
                            <label class="form-label">Counted Hours</label>
                            <input type="text" class="form-control" value="@manualWorkedHoursStr" @onchange="OnManualWorkedHoursChanged" placeholder="00:00" inputmode="numeric" />
                        </div>

                        <div class="col-12 col-md-6 col-lg-4 d-flex align-items-end">
                            <div class="form-check mb-2">
                                <input id="preview-checkbox-modal" type="checkbox" class="form-check-input" @bind="isPreview" />
                                <label class="form-check-label" for="preview-checkbox-modal">Preview work unit</label>
                            </div>
                        </div>

                        <div class="col-12">
                            <label class="form-label">Notes</label>
                            <textarea class="form-control" rows="2" @bind="notes"></textarea>
                        </div>
                    </div>

                    <hr />

                    <h3 class="h6">Computed values</h3>
                    <div class="row g-3">
                        <div class="col-6 col-md-4 col-lg-3">
                            <label class="form-label text-muted">Calculated Hours</label>
                            <div class="form-control-plaintext fw-bold">@FormatHours(calculatedWorkedHours)</div>
                        </div>
                        <div class="col-6 col-md-4 col-lg-3">
                            <label class="form-label text-muted">Difference</label>
                            <div class="form-control-plaintext fw-bold">@FormatSignedHours(workedHoursDelta)</div>
                        </div>
                        <div class="col-6 col-md-4 col-lg-3">
                            <label class="form-label text-muted">Gross Income</label>
                            <div class="form-control-plaintext fw-bold">€@grossIncome.ToString("N2")</div>
                        </div>
                        <div class="col-6 col-md-4 col-lg-3">
                            <label class="form-label text-muted">Net Income</label>
                            <div class="form-control-plaintext fw-bold">€@netIncome.ToString("N2")</div>
                        </div>
                    </div>

                    <div class="mt-4">
                        <h3 class="h6">Day Total</h3>
                        <div class="row g-3">
                            <div class="col-6 col-md-4 col-lg-3">
                                <label class="form-label text-muted">Total Hours For Day</label>
                                <div class="form-control-plaintext fw-bold">@FormatHours(dayTotalHours)</div>
                            </div>
                            <div class="col-6 col-md-4 col-lg-3">
                                <label class="form-label text-muted">Work Units Counted</label>
                                <div class="form-control-plaintext fw-bold">@dayWorkUnitCount</div>
                            </div>
                        </div>
                    </div>

                    @if (!string.IsNullOrWhiteSpace(statusMessage))
                    {
                        <div class="alert alert-danger py-2 mt-3 mb-0">@statusMessage</div>
                    }
                }
            </div>

            <div class="calendar-modal-actions">
                <button type="button" class="btn btn-primary" @onclick="SaveAsync" disabled="@(!loaded)">Save</button>
                @if (isExistingUnit)
                {
                    <button type="button" class="btn btn-outline-danger" @onclick="DeleteAsync">Delete</button>
                }
                <button type="button" class="btn btn-outline-secondary ms-auto" @onclick="CloseAsync">Cancel</button>
            </div>
        </div>
    </div>
</div>

@code {
    [Parameter] public DateOnly Date { get; set; }
    [Parameter] public string? UnitId { get; set; }
    [Parameter] public EventCallback OnSaved { get; set; }
    [Parameter] public EventCallback OnClosed { get; set; }

    private bool loaded;
    private DateOnly selectedDate;
    private string unitId = string.Empty;
    private string label = "Work unit";
    private WorkUnitLocation location = WorkUnitLocation.Office;
    private string? startTimeStr;
    private string? endTimeStr;
    private decimal manualWorkedHours;
    private string manualWorkedHoursStr = "00:00";
    private bool isPreview;
    private string? notes;
    private string? statusMessage;
    private bool isExistingUnit;
    private WorkDayDocument? selectedDay;
    private string? loadKey;

    private decimal grossIncome;
    private decimal netIncome;
    private decimal? calculatedWorkedHours;
    private decimal workedHoursDelta;
    private decimal dayTotalHours;
    private int dayWorkUnitCount;

    private AppSettingsDocument settings = new();

    protected override async Task OnParametersSetAsync()
    {
        var nextKey = $"{Date:yyyy-MM-dd}|{UnitId}";
        if (nextKey == loadKey)
        {
            return;
        }

        loadKey = nextKey;
        loaded = false;
        selectedDate = Date;
        statusMessage = null;
        settings = await AppSettingsService.GetAsync();
        await LoadUnitAsync();
        loaded = true;
    }

    private async Task LoadUnitAsync()
    {
        if (string.IsNullOrWhiteSpace(UnitId))
        {
            selectedDay = await WorkDayService.GetAsync(selectedDate);
            SetDefaults();
            return;
        }

        selectedDay = await WorkDayService.GetAsync(selectedDate);
        var existing = await WorkDayService.GetWorkUnitAsync(selectedDate, UnitId);
        if (existing is not null)
        {
            unitId = existing.Id;
            label = existing.Label;
            location = existing.Location;
            startTimeStr = existing.StartTime?.ToString("HH:mm");
            endTimeStr = existing.EndTime?.ToString("HH:mm");
            manualWorkedHours = existing.ManualWorkedHours;
            manualWorkedHoursStr = FormatDurationHours(existing.ManualWorkedHours);
            isPreview = existing.IsPreview;
            notes = existing.Notes;
            isExistingUnit = true;
        }
        else
        {
            SetDefaults();
            statusMessage = "The selected work unit was not found. A new unit will be created for this day.";
        }

        RecomputePreview();
    }

    private async Task OnDateChangedAsync(DateOnly? value)
    {
        if (value.HasValue)
        {
            selectedDate = value.Value;
            selectedDay = await WorkDayService.GetAsync(selectedDate);
            statusMessage = null;
            RecomputePreview();
        }
    }

    private Task OnStartTimeChanged(ChangeEventArgs e)
    {
        startTimeStr = e.Value?.ToString();
        SyncManualHoursToCalculated();
        statusMessage = null;
        return Task.CompletedTask;
    }

    private Task OnEndTimeChanged(ChangeEventArgs e)
    {
        endTimeStr = e.Value?.ToString();
        SyncManualHoursToCalculated();
        statusMessage = null;
        return Task.CompletedTask;
    }

    private Task OnManualWorkedHoursChanged(ChangeEventArgs e)
    {
        var rawValue = e.Value?.ToString();
        if (TryParseDurationHours(rawValue, out var parsedHours))
        {
            manualWorkedHours = parsedHours;
            manualWorkedHoursStr = FormatDurationHours(parsedHours);
        }
        else
        {
            manualWorkedHoursStr = FormatDurationHours(manualWorkedHours);
        }

        RecomputePreview();
        statusMessage = null;
        return Task.CompletedTask;
    }

    private void SetDefaults()
    {
        unitId = string.Empty;
        label = "Work unit";
        location = WorkUnitLocation.Office;
        startTimeStr = null;
        endTimeStr = null;
        manualWorkedHours = 0m;
        manualWorkedHoursStr = "00:00";
        isPreview = false;
        notes = null;
        isExistingUnit = false;
        RecomputePreview();
    }

    private void SyncManualHoursToCalculated()
    {
        var calculated = CalculateDuration(ParseTime(startTimeStr), ParseTime(endTimeStr));
        manualWorkedHours = calculated ?? 0m;
        manualWorkedHoursStr = FormatDurationHours(manualWorkedHours);
        RecomputePreview();
    }

    private void RecomputePreview()
    {
        calculatedWorkedHours = CalculateDuration(ParseTime(startTimeStr), ParseTime(endTimeStr));
        workedHoursDelta = manualWorkedHours - (calculatedWorkedHours ?? 0m);
        grossIncome = manualWorkedHours * settings.HourlyGrossRate;
        var taxableBase = grossIncome * settings.ProfitabilityCoefficient;
        netIncome = grossIncome - (taxableBase * settings.InpsRate) - (taxableBase * settings.SubstituteTaxRate);
        RecomputeDayTotals();
    }

    private async Task SaveAsync()
    {
        RecomputePreview();

        var workUnit = new WorkUnitDocument
        {
            Id = unitId,
            Label = label,
            Location = location,
            StartTime = ParseTime(startTimeStr),
            EndTime = ParseTime(endTimeStr),
            ManualWorkedHours = Math.Max(0m, manualWorkedHours),
            IsPreview = isPreview,
            Notes = notes
        };

        await WorkDayService.SaveWorkUnitAsync(selectedDate, workUnit);
        await OnSaved.InvokeAsync();
    }

    private async Task DeleteAsync()
    {
        if (!isExistingUnit || string.IsNullOrWhiteSpace(unitId))
        {
            return;
        }

        var confirmed = await JS.InvokeAsync<bool>("confirm", $"Delete work unit '{label}' on {FormatDisplayDate(selectedDate)}?\nThis cannot be undone.");
        if (!confirmed)
        {
            return;
        }

        var deleted = await WorkDayService.DeleteWorkUnitAsync(selectedDate, unitId);
        if (deleted)
        {
            await OnSaved.InvokeAsync();
            return;
        }

        statusMessage = "Unable to delete the work unit.";
    }

    private Task CloseAsync() => OnClosed.InvokeAsync();

    private void RecomputeDayTotals()
    {
        var existingUnits = selectedDay?.WorkUnits ?? [];
        dayTotalHours = existingUnits
            .Where(unit => !string.Equals(unit.Id, unitId, StringComparison.Ordinal))
            .Sum(unit => unit.ManualWorkedHours) + manualWorkedHours;

        dayWorkUnitCount = existingUnits
            .Where(unit => !string.Equals(unit.Id, unitId, StringComparison.Ordinal))
            .Count() + 1;
    }

    private static TimeOnly? ParseTime(string? value)
    {
        return !string.IsNullOrWhiteSpace(value) && TimeOnly.TryParse(value, out var parsed)
            ? parsed
            : null;
    }

    private static decimal? CalculateDuration(TimeOnly? startTime, TimeOnly? endTime)
    {
        if (!startTime.HasValue || !endTime.HasValue || endTime <= startTime)
        {
            return null;
        }

        return Math.Round((decimal)(endTime.Value - startTime.Value).TotalHours, 2, MidpointRounding.AwayFromZero);
    }

    private static string FormatDurationHours(decimal value)
    {
        var totalMinutes = (int)Math.Round(value * 60m, MidpointRounding.AwayFromZero);
        var hours = totalMinutes / 60;
        var minutes = totalMinutes % 60;
        return $"{hours:00}:{minutes:00}";
    }

    private static string FormatHours(decimal? value)
    {
        return value.HasValue ? DurationFormatter.FormatHours(value.Value) : "—";
    }

    private static string FormatSignedHours(decimal value)
    {
        if (value == 0m)
        {
            return "00:00";
        }

        var prefix = value > 0m ? "+" : "-";
        return prefix + DurationFormatter.FormatHours(Math.Abs(value));
    }

    private static bool TryParseDurationHours(string? value, out decimal hours)
    {
        hours = 0m;
        if (string.IsNullOrWhiteSpace(value))
        {
            return false;
        }

        if (TimeOnly.TryParse(value, out var parsedTime))
        {
            hours = parsedTime.Hour + (parsedTime.Minute / 60m);
            return true;
        }

        if (decimal.TryParse(value, out var parsedDecimal))
        {
            hours = Math.Max(0m, parsedDecimal);
            return true;
        }

        return false;
    }

    private static string FormatDisplayDate(DateOnly date)
    {
        return date.ToString("dddd dd/MM/yyyy");
    }
}