using System.Text.RegularExpressions;
using CareFix.Api.Infrastructure;
namespace CareFix.Api.Fixes;
/// Who must approve a fix. Kept separate from the service so it can be tested on its own.
public static class ApprovalRules
{
public static string RuleText(string risk) => risk switch
{
"Low" => "1 approval from a Support Lead or Support Head",
"Medium" => "2 approvals from different people: a Support Lead (or Head) and a Product Owner (or Head)",
_ => "Support Head approval plus the hospital's written consent file",
};
/// Whether this role may record an approval at all. Four-eyes and duplicate checks live in the service.
public static bool CanApprove(string role, string risk) => risk switch
{
"Low" => role is Roles.Lead or Roles.Head,
"Medium" => role is Roles.Lead or Roles.ProductOwner or Roles.Head,
_ => role is Roles.Head,
};
/// Whether the approvals collected so far are enough to run the fix.
public static bool IsSatisfied(string risk, IReadOnlyList approvals, bool hasConsent)
{
var ok = approvals.Where(a => a.Decision == "Approved").ToList();
return risk switch
{
"Low" => ok.Any(a => a.ApproverRole is Roles.Lead or Roles.Head),
"Medium" => ok.Where(a => a.ApproverRole is Roles.Lead or Roles.Head)
.Any(l => ok.Any(p => p.ApproverRole is Roles.ProductOwner or Roles.Head && p.ApproverId != l.ApproverId)),
_ => hasConsent && ok.Any(a => a.ApproverRole == Roles.Head),
};
}
}
/// How a fix's risk level is worked out.
public static partial class RiskRules
{
private static readonly string[] Order = ["Low", "Medium", "High"];
[GeneratedRegex("(AMT|AMOUNT|RATE|PRICE|TOTAL|NETAMT|NET_AMT|DISC|PAID|REFUND|BALANCE|DUE|QTY|QUANTITY|TAX|GST|CGST|SGST|DEPOSIT|ADVANCE)", RegexOptions.IgnoreCase)]
private static partial Regex AmountColumn();
/// Columns holding money or quantities are always High risk, whatever the dictionary says.
public static bool IsAmountColumn(string column) => AmountColumn().IsMatch(column);
public static string Max(string a, string b) => Array.IndexOf(Order, a) >= Array.IndexOf(Order, b) ? a : b;
}