#nullable enable
using Baya.Domain.Common;

namespace Baya.Domain.Entities.Booking;

/// <summary>
/// A config-driven, snapshot-able cancellation/refund tier keyed by initiating actor + lead-time bucket.
/// The applicable row is resolved at cancel time by <c>(applies_to, hours-before-start)</c> and its
/// <see cref="Code"/> + <see cref="RefundPercentage"/> are <b>frozen onto the booking</b> — a later edit to
/// the policy row must never change a past cancellation.
/// <para>
/// The penalty/fee is modelled as a flat IRR amount (<see cref="FeeAmountIrr"/>) <b>plus</b> an optional
/// fraction (<see cref="FeeRate"/>): a tier uses whichever is non-zero/non-null. At MVP the customer tiers
/// carry neither; the nurse-no-show penalty is modelled but its posting is deferred to payouts (b13).
/// </para>
/// </summary>
public class CancellationPolicy : BaseEntity<long>
{
    /// <summary>Stable unique code, e.g. <c>standard_24h</c>, <c>nurse_no_show</c>.</summary>
    public string Code { get; set; } = null!;

    /// <summary>Who the tier applies to — a <see cref="CancellationActor"/> code.</summary>
    public string AppliesTo { get; set; } = null!;

    /// <summary>Lower bound (inclusive) of the lead-time bucket in hours; null = open lower bound.</summary>
    public int? HoursBeforeStartMin { get; set; }

    /// <summary>Upper bound (exclusive) of the lead-time bucket in hours; null = open upper bound.</summary>
    public int? HoursBeforeStartMax { get; set; }

    /// <summary>Refund fraction of the un-started portion, 0–100.</summary>
    public decimal RefundPercentage { get; set; }

    /// <summary>Flat cancellation fee / nurse penalty (IRR). 0 = none.</summary>
    public long FeeAmountIrr { get; set; }

    /// <summary>Optional cancellation fee / nurse penalty as a fraction. Null = none.</summary>
    public decimal? FeeRate { get; set; }

    public bool IsActive { get; set; } = true;

    public DateTimeOffset? DeletedAt { get; set; }

    /// <summary>True when a lead time of <paramref name="hoursBeforeStart"/> falls in this tier's half-open
    /// bucket <c>[min, max)</c> (null bound = open).</summary>
    public bool Covers(double hoursBeforeStart)
        => (HoursBeforeStartMin is null || hoursBeforeStart >= HoursBeforeStartMin)
           && (HoursBeforeStartMax is null || hoursBeforeStart < HoursBeforeStartMax);
}

/// <summary>Stable seed codes for the baseline cancellation tiers.</summary>
public static class CancellationPolicyCode
{
    /// <summary>Customer, ≥ 24h before start → full refund.</summary>
    public const string Standard24h = "standard_24h";

    /// <summary>Customer, &lt; 24h before start → 50% refund.</summary>
    public const string StandardInside24h = "standard_inside_24h";

    /// <summary>Nurse no-show / nurse-initiated → full refund (+ nurse penalty, posted in b13).</summary>
    public const string NurseNoShow = "nurse_no_show";

    /// <summary>Admin-initiated → full refund, no penalty.</summary>
    public const string AdminCancellation = "admin_cancellation";
}
