/**
 * Behavior-Events: Onsite-Verhalten (Pageviews, Cart-Aktionen, Quote-Forms) auf
 * Workspace-Zielseiten. Wird sowohl vom mf-track.js-Snippet (`source='js'`) als auch
 * von Shop-Webhooks (`source='webhook'`) gefüttert.
 *
 * Best-Effort-Pattern wie `recordConversion` in campaign-repo.ts: wirft nie,
 * loggt nur. Behavior-Tracking ist nice-to-have — ein DB-Failure darf den
 * Beacon-/Webhook-Empfang nicht abbrechen.
 */

import { eq } from 'drizzle-orm';
import { db } from './client';
import { behaviorEvents, recipients, campaigns, campaignVariants, type BehaviorEventInsert } from './schema';

export type BehaviorType = NonNullable<BehaviorEventInsert['type']>;
export type BehaviorSource = NonNullable<BehaviorEventInsert['source']>;

export interface BehaviorEventInput {
  recipientId: string;
  /**
   * Optional: campaignId/variantId können vom Caller mitgegeben werden (z.B. Cookie-Inhalt).
   * Wenn nicht gesetzt, leiten wir sie aus dem Recipient ab (recipients.campaignId/variantId).
   * Der explizite Cookie-Wert hat Vorrang, weil ein Recipient theoretisch in mehreren
   * Container-Campaigns gleichzeitig sein kann (Trigger-Container-Sends).
   */
  campaignId?: string;
  variantId?: string;
  type: BehaviorType;
  url?: string;
  productId?: string;
  /** Geld-/Punkt-Wert in EUR-Cent für add_to_cart (z.B. 2990 = 29.90 €). */
  value?: number;
  durationMs?: number;
  scrollPct?: number;
  meta?: Record<string, unknown>;
  userAgent?: string;
  ipHash?: string;
  source: BehaviorSource;
}

export type BehaviorResult =
  | { ok: true; eventId: string }
  | { ok: false; reason: 'recipient_not_found' | 'recipient_no_campaign' | 'db_error' };

export function recordBehavior(input: BehaviorEventInput): BehaviorResult {
  try {
    const r = db
      .select({ campaignId: recipients.campaignId, variantId: recipients.variantId })
      .from(recipients)
      .where(eq(recipients.id, input.recipientId))
      .get();

    if (!r) return { ok: false, reason: 'recipient_not_found' };

    // Cookie-übermittelte Werte haben Vorrang. Wenn keine vom Caller kommen,
    // fallen wir auf den Recipient-Stamm zurück (typischer JS-Beacon-Pfad).
    const campaignId = input.campaignId ?? r.campaignId;
    const variantId = input.variantId ?? r.variantId ?? null;

    if (!campaignId) return { ok: false, reason: 'recipient_no_campaign' };

    // variantId validieren: falls Cookie eine variantId für eine andere Campaign
    // mitschickt, ignorieren wir das (verhindert Datenmüll bei Multi-Campaign-Sessions).
    let resolvedVariantId: string | null = variantId;
    if (variantId && input.campaignId) {
      const v = db
        .select({ id: campaignVariants.id })
        .from(campaignVariants)
        .where(eq(campaignVariants.id, variantId))
        .get();
      if (!v) resolvedVariantId = null;
    }

    // Sanity-Check: campaignId muss existieren (Cookie-Wert könnte alter Test sein)
    if (input.campaignId) {
      const c = db.select({ id: campaigns.id }).from(campaigns).where(eq(campaigns.id, campaignId)).get();
      if (!c) return { ok: false, reason: 'recipient_no_campaign' };
    }

    const eventId = crypto.randomUUID();
    db.insert(behaviorEvents)
      .values({
        id: eventId,
        campaignId,
        recipientId: input.recipientId,
        variantId: resolvedVariantId,
        type: input.type,
        url: input.url ?? null,
        productId: input.productId ?? null,
        value: input.value ?? null,
        durationMs: input.durationMs ?? null,
        scrollPct: input.scrollPct ?? null,
        metaJson: input.meta ?? null,
        userAgent: input.userAgent ?? null,
        ipHash: input.ipHash ?? null,
        source: input.source,
      })
      .run();

    return { ok: true, eventId };
  } catch (e) {
    console.error('[recordBehavior] failed:', e instanceof Error ? e.message : e);
    return { ok: false, reason: 'db_error' };
  }
}
