///|
/// A concrete, explainable retention action reviewed by operators.
pub struct Playbook {
  code : String
  priority : Int
  max_recency : Int
  min_frequency : Int
  action : String
  rationale : String
}

///|
/// The policy catalog is data-driven so adding a policy does not duplicate code.
pub fn retention_playbooks() -> Array[Playbook] {
  [
    {
      code: "winback-urgent-00",
      priority: 25,
      max_recency: 14,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-01",
      priority: 30,
      max_recency: 14,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-02",
      priority: 35,
      max_recency: 14,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-03",
      priority: 25,
      max_recency: 14,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-04",
      priority: 30,
      max_recency: 14,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-05",
      priority: 35,
      max_recency: 14,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-06",
      priority: 25,
      max_recency: 14,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-07",
      priority: 30,
      max_recency: 14,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
    {
      code: "winback-urgent-08",
      priority: 45,
      max_recency: 21,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-09",
      priority: 35,
      max_recency: 21,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-10",
      priority: 40,
      max_recency: 21,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-11",
      priority: 45,
      max_recency: 21,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-12",
      priority: 35,
      max_recency: 21,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-13",
      priority: 40,
      max_recency: 21,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-14",
      priority: 45,
      max_recency: 21,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-15",
      priority: 35,
      max_recency: 21,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
    {
      code: "winback-urgent-16",
      priority: 50,
      max_recency: 28,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-17",
      priority: 55,
      max_recency: 28,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-18",
      priority: 45,
      max_recency: 28,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-19",
      priority: 50,
      max_recency: 28,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-20",
      priority: 55,
      max_recency: 28,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-21",
      priority: 45,
      max_recency: 28,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-22",
      priority: 50,
      max_recency: 28,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-23",
      priority: 55,
      max_recency: 28,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
    {
      code: "winback-urgent-24",
      priority: 55,
      max_recency: 35,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-25",
      priority: 60,
      max_recency: 35,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-26",
      priority: 65,
      max_recency: 35,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-27",
      priority: 55,
      max_recency: 35,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-28",
      priority: 60,
      max_recency: 35,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-29",
      priority: 65,
      max_recency: 35,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-30",
      priority: 55,
      max_recency: 35,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-31",
      priority: 60,
      max_recency: 35,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
    {
      code: "winback-urgent-32",
      priority: 75,
      max_recency: 42,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-33",
      priority: 65,
      max_recency: 42,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-34",
      priority: 70,
      max_recency: 42,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-35",
      priority: 75,
      max_recency: 42,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-36",
      priority: 65,
      max_recency: 42,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-37",
      priority: 70,
      max_recency: 42,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-38",
      priority: 75,
      max_recency: 42,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-39",
      priority: 65,
      max_recency: 42,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
    {
      code: "winback-urgent-40",
      priority: 80,
      max_recency: 49,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-41",
      priority: 85,
      max_recency: 49,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-42",
      priority: 75,
      max_recency: 49,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-43",
      priority: 80,
      max_recency: 49,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-44",
      priority: 85,
      max_recency: 49,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-45",
      priority: 75,
      max_recency: 49,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-46",
      priority: 80,
      max_recency: 49,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-47",
      priority: 85,
      max_recency: 49,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
    {
      code: "winback-urgent-48",
      priority: 85,
      max_recency: 56,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-49",
      priority: 90,
      max_recency: 56,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-50",
      priority: 95,
      max_recency: 56,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-51",
      priority: 85,
      max_recency: 56,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-52",
      priority: 90,
      max_recency: 56,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-53",
      priority: 95,
      max_recency: 56,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-54",
      priority: 85,
      max_recency: 56,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-55",
      priority: 90,
      max_recency: 56,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
    {
      code: "winback-urgent-56",
      priority: 105,
      max_recency: 63,
      min_frequency: 1,
      action: "send a personal win-back message",
      rationale: "high churn risk with a recent repeat history",
    },
    {
      code: "winback-value-57",
      priority: 95,
      max_recency: 63,
      min_frequency: 2,
      action: "offer a targeted value bundle",
      rationale: "high churn risk with meaningful spend",
    },
    {
      code: "service-review-58",
      priority: 100,
      max_recency: 63,
      min_frequency: 3,
      action: "assign a retention owner",
      rationale: "high risk requires human follow-up",
    },
    {
      code: "feedback-loop-59",
      priority: 105,
      max_recency: 63,
      min_frequency: 4,
      action: "request feedback before the next offer",
      rationale: "low frequency needs diagnosis",
    },
    {
      code: "loyalty-60",
      priority: 95,
      max_recency: 63,
      min_frequency: 1,
      action: "invite to the referral program",
      rationale: "healthy repeat behavior can create referrals",
    },
    {
      code: "early-access-61",
      priority: 100,
      max_recency: 63,
      min_frequency: 2,
      action: "offer early access to a relevant release",
      rationale: "high-value loyal behavior",
    },
    {
      code: "onboarding-62",
      priority: 105,
      max_recency: 63,
      min_frequency: 3,
      action: "complete the onboarding sequence",
      rationale: "new customers need activation",
    },
    {
      code: "education-63",
      priority: 95,
      max_recency: 63,
      min_frequency: 4,
      action: "send a product education sequence",
      rationale: "low activity can be addressed with guidance",
    },
  ]
}

///|
/// Select policies applicable to one customer insight.
pub fn applicable_playbooks(
  item : CustomerInsight,
  catalog : Array[Playbook],
) -> Array[Playbook] {
  catalog.filter(policy => {
    item.churn.score >= policy.priority &&
    item.scores.recency <= policy.max_recency &&
    item.scores.frequency >= policy.min_frequency
  })
}

///|
/// Choose the highest-priority policy for a customer, if one applies.
pub fn best_playbook(
  item : CustomerInsight,
  catalog : Array[Playbook],
) -> Playbook? {
  let applicable = applicable_playbooks(item, catalog)
  if applicable.is_empty() {
    None
  } else {
    let ranked = applicable.copy()
    ranked.sort_by((left, right) => right.priority - left.priority)
    Some(ranked[0])
  }
}