/**
 * Review effect interpreter.
 *
 * Only place where DB writes, push, and AI prescore are invoked for the review aggregate.
 *
 * Ordering (matches machine.ts emission order):
 *   1. insert-review (returns review row)
 *   2. update-vendor-aggregate (incremental weighted average)
 *   3. send-vendor-push (best-effort; logged via captureCaught upstream if it throws)
 *   4. update-review-reply
 *   5. mark-removal-requested
 *   6. insert-removal-queue (returns queue row id)
 *   7. enqueue-ai-prescore (fire-and-forget via injected callback)
 */

import { eq, sql } from 'drizzle-orm';
import type { DrizzleClient } from '@/server/db/client.js';
import type { PushClient } from '@/server/push/types.js';
import * as reviewQueries from '@/server/db/queries/reviews.js';
import { vendors, reviewRemovalQueue } from '@/server/db/schema.js';
import type { ReviewEffect } from './effects.js';

export interface ApplyReviewDeps {
  db: DrizzleClient;
  push: PushClient;
  /** Called once after insert-removal-queue if an `enqueue-ai-prescore` effect is present.  */
  schedulePrescore?: (queueId: string) => void;
}

export interface ApplyReviewResult {
  reviewId?: string;
  removalQueueId?: string;
}

export async function applyEffects(
  deps: ApplyReviewDeps,
  effects: ReviewEffect[],
): Promise<ApplyReviewResult> {
  const { db } = deps;
  const result: ApplyReviewResult = {};

  for (const eff of effects) {
    switch (eff.kind) {
      case 'insert-review': {
        const row = await reviewQueries.create(db, {
          purchaseId: eff.purchaseId,
          userId: eff.userId,
          vendorId: eff.vendorId,
          dealId: eff.dealId,
          reviewType: eff.reviewType,
          rating: eff.rating,
          body: eff.body,
        });
        result.reviewId = row.id;
        break;
      }
      case 'update-vendor-aggregate': {
        await db
          .update(vendors)
          .set({
            reviewsScore: sql`
              ROUND(
                (${vendors.reviewsScore} * ${vendors.reviewsCount} + ${eff.rating}) /
                (${vendors.reviewsCount} + 1),
                2
              )
            `,
            reviewsCount: sql`${vendors.reviewsCount} + 1`,
          })
          .where(eq(vendors.id, eff.vendorId));
        break;
      }
      case 'send-vendor-push': {
        await deps.push.sendToVendor(eff.vendorId, {
          title: 'New review',
          body: `Rating: ${eff.rating}/5 - ${eff.bodyPreview}`,
          url: `/vendor/reviews`,
          tag: 'new_review',
        });
        break;
      }
      case 'update-review-reply': {
        await reviewQueries.reply(db, eff.reviewId, eff.reply);
        break;
      }
      case 'mark-removal-requested': {
        await reviewQueries.requestRemoval(db, eff.reviewId, eff.reason);
        break;
      }
      case 'insert-removal-queue': {
        const [queueRow] = await db
          .insert(reviewRemovalQueue)
          .values({
            reviewId: eff.reviewId,
            submittedBy: eff.submittedBy,
            reason: eff.reason,
          })
          .returning({ id: reviewRemovalQueue.id });
        result.removalQueueId = queueRow?.id;
        break;
      }
      case 'enqueue-ai-prescore': {
        if (result.removalQueueId && deps.schedulePrescore) {
          deps.schedulePrescore(result.removalQueueId);
        }
        break;
      }
    }
  }

  return result;
}
