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 } });
}

// ─── Onboarding-Step ────────────────────────────────────────────────────────

// Onboarding-Milestones im Duo-Style-Flow (siehe app/onboarding/index.tsx):
//   welcome        → Flow noch nicht angefangen (Default für neue Profile)
//   account        → Nickname gesetzt
//   plan           → Trial/Sub gewählt (vor Payment)
//   pre_protection → Payment confirmed, Protection-Slide noch offen
//   done           → komplett abgeschlossen
//
// Legacy-Werte 'nickname' und 'block' werden im Backend noch akzeptiert für
// Backwards-Compat (alte Builds in TestFlight), aber im neuen Flow nicht mehr
// geschrieben. Können nach allen-User-Force-Update entfernt werden.
export const ONBOARDING_STEPS = [
  "welcome",
  "account",
  "plan",
  "pre_protection",
  "done",
  // legacy (kept readable to not break old clients):
  "nickname",
  "block",
] as const;
export type OnboardingStep = (typeof ONBOARDING_STEPS)[number];

export function isOnboardingStep(value: unknown): value is OnboardingStep {
  return typeof value === "string" && (ONBOARDING_STEPS as readonly string[]).includes(value);
}

export async function setOnboardingStep(userId: string, step: OnboardingStep) {
  const db = usePrisma();
  return db.profile.update({
    where: { id: userId },
    data: { onboardingStep: step },
    select: { onboardingStep: true },
  });
}

// ─── DiGA-Demographie ──────────────────────────────────────────────────────

export type DemographicsFields = {
  birthYear: number | null;
  gender: string | null;
  maritalStatus: string | null;
  // profession is legacy — kept for backwards-compat with old frontend versions
  profession: string | null;
  employmentStatus: string | null;
  shiftWork: boolean | null;
  industry: string | null;
  jobTenure: string | null;
  bundesland: string | null;
  city: string | null;
};

export type DemographicsPatch = Partial<DemographicsFields>;

/**
 * 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<string, unknown> = { ...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,       // legacy field — also cleared for completeness
      employmentStatus: null,
      shiftWork: null,
      industry: null,
      jobTenure: null,
      bundesland: null,
      city: null,
      demographicsWithdrawnAt: new Date(),
      // demographicsConsentAt bleibt — Audit-Trail dass User mal eingewilligt hat
    },
  });
}

/** Read demographic fields + consent-state for the current user. */
export async function getDemographics(userId: string) {
  const db = usePrisma();
  const row = await db.profile.findUnique({
    where: { id: userId },
    select: {
      birthYear: true,
      gender: true,
      maritalStatus: true,
      employmentStatus: true,
      shiftWork: true,
      industry: true,
      jobTenure: true,
      bundesland: true,
      city: true,
      demographicsConsentAt: true,
      demographicsWithdrawnAt: true,
    },
  });
  if (!row) throw createError({ statusCode: 404, message: "Profil nicht gefunden" });
  return {
    birthYear: row.birthYear,
    gender: row.gender,
    maritalStatus: row.maritalStatus,
    employmentStatus: row.employmentStatus,
    shiftWork: row.shiftWork,
    industry: row.industry,
    jobTenure: row.jobTenure,
    bundesland: row.bundesland,
    city: row.city,
    consentAt: row.demographicsConsentAt?.toISOString() ?? null,
    withdrawnAt: row.demographicsWithdrawnAt?.toISOString() ?? null,
  };
}

// ─── 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,
        employmentStatus: 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 };

    // Core 6 fields must be non-null/non-empty (employmentStatus replaces profession)
    const requiredFilled =
      profile.birthYear != null &&
      !!profile.gender &&
      !!profile.maritalStatus &&
      !!profile.employmentStatus &&
      !!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 };
  });
}

// ─── Lyra Voice-Picker ────────────────────────────────────────────────────

const ALLOWED_LYRA_VOICE_IDS = [
  null,
  "iFSsEDGbm0FiEd2IVH4w", // Voice 1
  "Gt7OshJCH7MuzX96wFHi", // Voice 2
] as const;

export type LyraVoiceId = (typeof ALLOWED_LYRA_VOICE_IDS)[number];

export function isAllowedLyraVoiceId(value: unknown): value is LyraVoiceId {
  return (ALLOWED_LYRA_VOICE_IDS as readonly unknown[]).includes(value);
}

export async function setLyraVoiceId(userId: string, voiceId: LyraVoiceId) {
  const db = usePrisma();
  return db.profile.update({
    where: { id: userId },
    data: { lyraVoiceId: voiceId },
    select: { lyraVoiceId: true },
  });
}

// ─── 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() },
  });
}

// ─── Following List ───────────────────────────────────────────────────────

/** Return IDs of all users that currentUser follows. No pagination — rarely >1k. */
export async function getFollowingIds(currentUserId: string): Promise<string[]> {
  const db = usePrisma();
  const rows = await db.userFollow.findMany({
    where: { followerId: currentUserId },
    select: { followingId: true },
  });
  return rows.map((r) => r.followingId);
}

// ─── Presence / Online-Status ─────────────────────────────────────────────
//
// NOTE: lastSeenAt is added in migration 20260518_add_last_seen_at.
// Prisma client is regenerated on staging after `prisma migrate deploy`.
// Until then the generated client type doesn't include lastSeenAt — raw SQL
// is used here to avoid breaking the tsc build on pre-migration client types.

/**
 * Touch lastSeenAt for the authenticated user (Heartbeat, Phase 1).
 * Phase 2: replaced by Supabase Edge-Function on presence-leave events.
 */
export async function touchLastSeen(userId: string): Promise<Date> {
  const db = usePrisma();
  const now = new Date();
  await db.$executeRaw`
    UPDATE "rebreak"."profiles"
    SET "last_seen_at" = ${now}
    WHERE "id" = ${userId}::uuid
  `;
  return now;
}

/**
 * Batch-fetch lastSeenAt for up to 50 user IDs.
 *
 * Privacy filters applied:
 *   1. Target's presenceVisible must be true (opt-out respected).
 *   2. Requester must follow the target (UserFollow row must exist).
 *
 * Targets failing either condition are silently omitted — caller maps them
 * to null in the response, which the frontend interprets as "no indicator".
 */
export async function getLastSeenBatch(
  currentUserId: string,
  userIds: string[],
): Promise<{ id: string; lastSeenAt: Date | null }[]> {
  const db = usePrisma();

  // Step 1: which of the requested IDs does currentUser actually follow?
  const follows = await db.userFollow.findMany({
    where: { followerId: currentUserId, followingId: { in: userIds } },
    select: { followingId: true },
  });
  const followingSet = new Set(follows.map((f) => f.followingId));

  // Step 2: only query profiles that are followed AND have visibility enabled
  const visibleIds = userIds.filter((id) => followingSet.has(id));
  if (visibleIds.length === 0) return [];

  const rows = await db.$queryRaw<{ id: string; last_seen_at: Date | null }[]>`
    SELECT "id", "last_seen_at"
    FROM "rebreak"."profiles"
    WHERE "id" = ANY(${visibleIds}::uuid[])
      AND "presence_visible" = true
  `;
  return rows.map((r) => ({ id: r.id, lastSeenAt: r.last_seen_at }));
}

/** Update presence_visible opt-out toggle for a user. */
export async function setPresenceVisible(
  userId: string,
  visible: boolean,
): Promise<{ presenceVisible: boolean }> {
  const db = usePrisma();
  // Raw SQL — presenceVisible added in 20260518_add_presence_fields,
  // Prisma client regenerated on staging after migrate deploy.
  await db.$executeRaw`
    UPDATE "rebreak"."profiles"
    SET "presence_visible" = ${visible}
    WHERE "id" = ${userId}::uuid
  `;
  return { presenceVisible: visible };
}
