/* ===================================================================== lib/supporter-cycle.js — Reine Rechenlogik für den 30-Monats-Prämienzyklus. 1:1 portiert, unverändert (keine D1-/Netzwerkabhängigkeit). ===================================================================== */ export const CYCLE_LENGTH = 30; export const MILESTONES = [ { month: 6, type: "teddy_klein", labelDe: "Kleiner Hasen-Teddy" }, { month: 12, type: "teddy_mittel", labelDe: "Mittelgroßer Hasen-Teddy" }, { month: 18, type: "teddy_gross", labelDe: "Großer Hasen-Teddy" }, { month: 24, type: "tasse_karte", labelDe: "Tasse & Autogrammkarte" }, { month: 30, type: "hoodie", labelDe: "Exklusiver Hoodie" }, ]; export function computeProgress(totalPaidMonths) { const total = Math.max(0, Math.floor(totalPaidMonths || 0)); if (total === 0) return { totalPaidMonths: 0, completedCycles: 0, cycleNumber: 1, currentCycleMonth: 0 }; const completedCycles = Math.floor((total - 1) / CYCLE_LENGTH); const currentCycleMonth = total - completedCycles * CYCLE_LENGTH; return { totalPaidMonths: total, completedCycles, cycleNumber: completedCycles + 1, currentCycleMonth }; } export function reachedMilestonesInCurrentCycle(currentCycleMonth) { return MILESTONES.filter((m) => currentCycleMonth >= m.month); } export function nextMilestone(currentCycleMonth) { return MILESTONES.find((m) => currentCycleMonth < m.month) || null; } export function allUnlockedMilestones(totalPaidMonths) { const { completedCycles, currentCycleMonth } = computeProgress(totalPaidMonths); const unlocked = []; for (let c = 1; c <= completedCycles; c++) { for (const m of MILESTONES) unlocked.push({ cycleNumber: c, ...m }); } const cycleNumber = completedCycles + 1; for (const m of reachedMilestonesInCurrentCycle(currentCycleMonth)) { unlocked.push({ cycleNumber, ...m }); } return unlocked; } export function timelineForCurrentCycle(totalPaidMonths) { const { currentCycleMonth, cycleNumber } = computeProgress(totalPaidMonths); return { cycleNumber, currentCycleMonth, milestones: MILESTONES.map((m) => ({ ...m, reached: currentCycleMonth >= m.month, isNext: nextMilestone(currentCycleMonth)?.month === m.month, monthsRemaining: Math.max(0, m.month - currentCycleMonth), })), }; }