import { usePrisma } from "../utils/prisma"; export async function getProfile(userId: string) { const db = usePrisma(); return db.profile.findUnique({ where: { id: userId } }); } export async function updateProfile( userId: string, data: Partial<{ username: string | null; nickname: string | null; avatar: string | null; }>, ) { const db = usePrisma(); return db.profile.update({ where: { id: userId }, data }); } export async function deleteProfile(userId: string) { const db = usePrisma(); return db.profile.delete({ where: { id: userId } }); } // ─── DiGA-Demographie ────────────────────────────────────────────────────── export type DemographicsFields = { birthYear: number | null; gender: string | null; maritalStatus: string | null; profession: string | null; bundesland: string | null; city: string | null; }; export type DemographicsPatch = Partial; /** * Update demographic fields. Sets `demographicsConsentAt = NOW()` on first * non-null write. Returns full updated row. */ export async function updateDemographics( userId: string, patch: DemographicsPatch, ) { const db = usePrisma(); const data: Record = { ...patch }; // First-touch consent stamp: only set if currently null AND at least one // non-null field is being written. Read-modify-write inside a tx so two // concurrent updates don't race the consent stamp. return db.$transaction(async (tx) => { const current = await tx.profile.findUnique({ where: { id: userId }, select: { demographicsConsentAt: true, demographicsWithdrawnAt: true, }, }); if (!current) { throw createError({ statusCode: 404, message: "Profil nicht gefunden" }); } const hasAnyValue = Object.values(patch).some( (v) => v !== null && v !== undefined, ); if (hasAnyValue && !current.demographicsConsentAt) { data.demographicsConsentAt = new Date(); } // Re-grant after withdrawal: clear withdrawn marker if (hasAnyValue && current.demographicsWithdrawnAt) { data.demographicsWithdrawnAt = null; } return tx.profile.update({ where: { id: userId }, data }); }); } /** Withdraw demographics — null all fields, stamp withdrawal, keep consent-audit. */ export async function withdrawDemographics(userId: string) { const db = usePrisma(); return db.profile.update({ where: { id: userId }, data: { birthYear: null, gender: null, maritalStatus: null, profession: null, bundesland: null, city: null, demographicsWithdrawnAt: new Date(), // demographicsConsentAt bleibt — Audit-Trail dass User mal eingewilligt hat }, }); } // ─── Pro-Trial-Reward ────────────────────────────────────────────────────── export const PRO_TRIAL_DAYS = 7; /** * Award a 7-day Pro trial — only if all 6 demographic fields filled, * plan is currently 'free', and trial has never been used. * * Idempotent. Returns the awarded trial record or null if not eligible. */ export async function tryAwardProTrial( userId: string, source = "demographics_complete", ): Promise<{ trialAwarded: boolean; expiresAt: Date | null }> { const db = usePrisma(); return db.$transaction(async (tx) => { const profile = await tx.profile.findUnique({ where: { id: userId }, select: { plan: true, proTrialUsedAt: true, birthYear: true, gender: true, maritalStatus: true, profession: true, bundesland: true, city: true, }, }); if (!profile) return { trialAwarded: false, expiresAt: null }; // Once-per-user if (profile.proTrialUsedAt) return { trialAwarded: false, expiresAt: null }; // Already paid plan → no trial needed const plan = (profile.plan ?? "free").toLowerCase(); if (plan !== "free") return { trialAwarded: false, expiresAt: null }; // All 6 fields must be non-null/non-empty const requiredFilled = profile.birthYear != null && !!profile.gender && !!profile.maritalStatus && !!profile.profession && !!profile.bundesland && !!profile.city; if (!requiredFilled) return { trialAwarded: false, expiresAt: null }; const startedAt = new Date(); const expiresAt = new Date( startedAt.getTime() + PRO_TRIAL_DAYS * 24 * 60 * 60 * 1000, ); await tx.profile.update({ where: { id: userId }, data: { plan: "pro", proTrialStartedAt: startedAt, proTrialExpiresAt: expiresAt, proTrialSource: source, proTrialUsedAt: startedAt, }, }); return { trialAwarded: true, expiresAt }; }); } // ─── Banner / Install-Event ──────────────────────────────────────────────── export async function dismissDigaBanner(userId: string) { const db = usePrisma(); return db.profile.update({ where: { id: userId }, data: { digaBannerDismissedAt: new Date() }, }); } export async function recordInstallEvent(userId: string) { const db = usePrisma(); return db.profile.update({ where: { id: userId }, data: { lastInstallAt: new Date() }, }); }