@namespace Lumeo

@* Inline SVGs sized to the checkbox's 12-unit viewBox. Stroke-width ~2.75 gives a crisper check
   than Lucide's default 2 at this size, and `stroke-linecap/join="round"` kills the pixel-corner
   sharpness the user was seeing. *@

@if (Label is not null)
{
    <div class="flex items-start gap-2.5">
        <button type="button" id="@_id" role="checkbox" aria-checked="@AriaChecked"
                aria-required="@Required.ToString().ToLowerInvariant()"
                aria-invalid="@EffectiveInvalid.ToString().ToLowerInvariant()"
                class="@CssClass" @onclick="Toggle" disabled="@Disabled" @attributes="AdditionalAttributes">
            @RenderIcon()
        </button>
        <div class="grid gap-1 leading-none pt-[1px]">
            <label for="@_id" class="text-sm font-medium leading-none cursor-pointer peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
                @Label
                @if (Required)
                {
                    <span class="text-destructive ml-0.5">*</span>
                }
            </label>
            @if (!string.IsNullOrEmpty(Description))
            {
                <p class="text-xs text-muted-foreground">@Description</p>
            }
        </div>
    </div>
}
else
{
    <button type="button" id="@_id" role="checkbox" aria-checked="@AriaChecked" aria-label="@AriaLabel"
            aria-required="@Required.ToString().ToLowerInvariant()"
            aria-invalid="@EffectiveInvalid.ToString().ToLowerInvariant()"
            class="@CssClass" @onclick="Toggle" disabled="@Disabled" @attributes="AdditionalAttributes">
        @RenderIcon()
    </button>
}

@if (!InsideFormField)
{
    @if (EffectiveInvalid && !string.IsNullOrEmpty(ErrorText))
    {
        <p class="mt-1 text-xs text-destructive">@ErrorText</p>
    }
    else if (!string.IsNullOrEmpty(HelperText))
    {
        <p class="mt-1 text-xs text-muted-foreground">@HelperText</p>
    }
}

@code {
    private RenderFragment RenderIcon() => __builder =>
    {
        if (IsIndeterminate)
        {
            <svg class="pointer-events-none h-2.5 w-2.5" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round">
                <line x1="3" y1="6" x2="9" y2="6" />
            </svg>
        }
        else if (Checked)
        {
            <svg class="pointer-events-none h-2.5 w-2.5" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2.75" stroke-linecap="round" stroke-linejoin="round">
                <polyline points="2.5,6.5 5,8.5 9.5,3.5" />
            </svg>
        }
    };
}

@code {
    [CascadingParameter] private FormField.FormFieldContext? FormField { get; set; }

    [Parameter] public bool Checked { get; set; }
    [Parameter] public EventCallback<bool> CheckedChanged { get; set; }
    [Parameter] public bool IsIndeterminate { get; set; }
    [Parameter] public EventCallback<bool> IsIndeterminateChanged { get; set; }
    [Parameter] public bool Disabled { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public string? Description { get; set; }
    [Parameter] public string? Id { get; set; }
    /// <summary>
    /// Accessible name used when <see cref="Label"/> is not set (the checkbox
    /// renders without a visible label). Required for WCAG compliance when
    /// the checkbox sits alone in a cell or alongside text that isn't linked
    /// to it via <c>for</c> / <c>htmlFor</c>.
    /// </summary>
    [Parameter] public string? AriaLabel { get; set; }

    /// <summary>
    /// When true, the checkmark draws itself in via stroke-dasharray and the
    /// box plays a subtle 200ms scale bounce on check. Default is <c>false</c>.
    /// </summary>
    [Parameter] public bool Animated { get; set; }

    /// <summary>
    /// When true, triggers a short haptic vibration via <c>navigator.vibrate()</c>
    /// each time the checkbox state changes. Silently no-ops on browsers without the
    /// Vibration API (e.g. iOS Safari). Default is <c>false</c>.
    /// </summary>
    [Parameter] public bool HapticFeedback { get; set; }

    [Inject] private HapticsService Haptics { get; set; } = default!;

    [Parameter] public bool Required { get; set; }
    [Parameter] public bool Invalid { get; set; }
    [Parameter] public string? ErrorText { get; set; }
    [Parameter] public string? HelperText { get; set; }
    [Parameter] public string? Name { get; set; }
    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private bool EffectiveInvalid => Invalid || FormField?.HasError == true;
    private bool InsideFormField => FormField is not null;

    private string _id = default!;

    protected override void OnInitialized()
    {
        _id = Id ?? $"checkbox-{Guid.NewGuid():N}";
    }

    private string AriaChecked => IsIndeterminate ? "mixed" : Checked.ToString().ToLower();

    private string CssClass => string.Join(" ", new[]
    {
        "peer inline-flex items-center justify-center h-4 w-4 shrink-0 cursor-pointer rounded transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
        EffectiveInvalid
            ? (Checked || IsIndeterminate ? "bg-destructive border border-destructive text-primary-foreground focus-visible:ring-destructive/50" : "border border-destructive focus-visible:ring-destructive/50")
            : (Checked || IsIndeterminate ? "bg-primary border border-primary text-primary-foreground focus-visible:ring-ring" : "border border-primary/60 bg-background hover:border-primary focus-visible:ring-ring"),
        Animated && Checked ? "lumeo-checkbox-animated" : null,
        Class
    }.Where(s => !string.IsNullOrWhiteSpace(s)));

    private async Task Toggle()
    {
        if (!Disabled)
        {
            if (IsIndeterminate)
            {
                IsIndeterminate = false;
                await IsIndeterminateChanged.InvokeAsync(false);
                Checked = true;
                await CheckedChanged.InvokeAsync(true);
            }
            else
            {
                Checked = !Checked;
                await CheckedChanged.InvokeAsync(Checked);
            }
            if (HapticFeedback) await Haptics.Light();
        }
    }
}
