using BigRegister.Api.Contracts;
using BigRegister.Api.Data;

namespace BigRegister.Domain.Authorization;

public enum PrincipalRole { Drafter, Approver, Admin }

/// <summary>
/// The acting identity for this request. dev stub — NOT a security boundary: resolved
/// from the client-asserted X-Role header (mirrors the FE's `?role=` toggle). A real
/// system builds this from verified AD/OIDC claims (PRD-0002 §3, §7); everything else
/// in this file — the capability model, the single Authz.Can check both emitting and
/// enforcing — carries over unchanged once that swap happens.
/// </summary>
public sealed record Principal(PrincipalRole Role);

public enum BriefAction { Approve, Reject, Send }

/// <summary>
/// Single source of truth for brief authorization (PRD-0002 phase P1). The SAME
/// check both computes the decision flags shipped on the screen DTO (emit) and
/// gates the mutation endpoints (enforce) — so the two can never drift, closing the
/// classic broken-object-level-authorization gap (PRD-0002 §7).
/// </summary>
public static class Authz
{
  public static Principal ResolvePrincipal(HttpContext ctx) => new(ctx.Request.Headers["X-Role"].ToString() switch
  {
    "approver" => PrincipalRole.Approver,
    "admin" => PrincipalRole.Admin,
    _ => PrincipalRole.Drafter,
  });

  public static string ActingId(Principal principal) => principal.Role switch
  {
    PrincipalRole.Approver => BriefStore.ApproverId,
    PrincipalRole.Admin => BriefStore.AdminId,
    _ => BriefStore.DrafterId,
  };

  /// Coarse, resource-independent capabilities for `GET /me` (nav/menu-level — NOT
  /// tied to any specific brief's live status; contrast Decisions below).
  public static IReadOnlyList<string> RoleCapabilities(Principal principal) => principal.Role switch
  {
    PrincipalRole.Approver => new[] { "brief:approve", "brief:reject", "brief:send" },
    PrincipalRole.Admin => new[] { "orgtemplate:edit" },
    _ => Array.Empty<string>(),
  };

  /// Role + four-eyes (SoD) check only — no status. This is the exact check
  /// BriefStore.Review enforces before its status guard; kept separate from
  /// Decisions() below so enforcement ORDER (Forbidden before Conflict) matches
  /// today's behavior exactly. The explicit Approver condition keeps the new Admin
  /// role out of the review flow (WP-23) — SoD alone would have let it through.
  public static bool CanActOn(BriefAction action, Principal principal, string drafterId) => action switch
  {
    BriefAction.Approve or BriefAction.Reject =>
        principal.Role == PrincipalRole.Approver && ActingId(principal) != drafterId,
    BriefAction.Send => true, // sending is a mechanical dispatch step, not role-gated today
    _ => false,
  };

  /// Org-template management (WP-23): admin-only, resource-independent — templates
  /// have no per-resource state to weigh, so role IS the whole decision here.
  public static bool CanManageOrgTemplates(Principal principal) => principal.Role == PrincipalRole.Admin;

  /// Resource-aware decision for the screen DTO: "would this action succeed right
  /// now" — role/SoD AND the brief's current status. This is what the UI renders;
  /// it never re-derives these booleans itself.
  public static BriefDecisionsDto Decisions(Principal principal, string status, string drafterId) => new(
      CanEdit: principal.Role == PrincipalRole.Drafter && status is "draft" or "rejected",
      CanApprove: CanActOn(BriefAction.Approve, principal, drafterId) && status == "submitted",
      CanReject: CanActOn(BriefAction.Reject, principal, drafterId) && status == "submitted",
      CanSend: CanActOn(BriefAction.Send, principal, drafterId) && status == "approved");
}
