/**
 * Admin review-moderation resource — write actions.
 *
 * - approveRemoval: hide review, increment vendor counter, audit, email vendor.
 * - rejectRemoval: mark request rejected, audit, email vendor.
 * - prescoreViaAi: run AI agent scoring, store result in ai_score column.
 */

import { moderateReview, isReviewNotFoundError } from '@platform-modules/commerce-reviews';
import { eq } from 'drizzle-orm';
import { reviewRemovalQueue, reviews, vendors } from '@/server/db/schema.js';
import { toReviewsDb } from '@/server/reviews/reviews-platform.js';
import type { DrizzleClient } from '@/server/db/client.js';

/**
 * Mirror a host moderation decision onto the platform review store. A removal-queue
 * entry can reference a review absent from the platform store (a TECHNICAL review, or a
 * STANDARD review not yet seeded) — in that case the platform throws ReviewNotFoundError,
 * which is the correct no-op for a sync mirror and must NOT abort the host flow.
 * Any other error is a real failure and is rethrown.
 */
async function mirrorModeration(
  db: DrizzleClient,
  reviewId: string,
  decision: 'approve' | 'reject',
): Promise<void> {
  try {
    await moderateReview(toReviewsDb(db), reviewId, decision);
  } catch (err) {
    if (!isReviewNotFoundError(err)) throw err;
  }
}
import { sendReviewRemovalApproved, sendReviewRemovalRejected } from '@/server/services/email';
import { captureCaught } from '@/server/observability/capture.server';
import { ReviewRemovalError } from './errors.js';
import type { ReviewRemovalDeps } from './types.js';
import { recordAdminAction } from '@/server/db/queries/admin-actions.js';
import {
  setReviewRemovalAiScore,
  setReviewRemovalDecision,
} from '@/server/db/queries/review-removal-queue.js';
import { approveReviewRemoval, rejectReviewRemoval } from '@/server/db/queries/reviews.js';
import { incrementVendorRemovedReviewsCount } from '@/server/db/queries/vendors.js';

// ─── approveRemoval ───────────────────────────────────────────────────────────

export async function approveRemoval(
  deps: ReviewRemovalDeps,
  input: { adminId: string; removalId: string },
): Promise<void> {
  const { db } = deps;
  const { adminId, removalId } = input;

  const [entry] = await db
    .select({
      id: reviewRemovalQueue.id,
      reviewId: reviewRemovalQueue.reviewId,
      status: reviewRemovalQueue.status,
    })
    .from(reviewRemovalQueue)
    .where(eq(reviewRemovalQueue.id, removalId))
    .limit(1);

  if (!entry) {
    throw new ReviewRemovalError('NOT_FOUND', 'Removal request not found');
  }
  if (entry.status !== 'PENDING') {
    throw new ReviewRemovalError('INVALID_STATE', `Request is already ${entry.status}`);
  }

  const now = new Date();

  // Hide the review
  await approveReviewRemoval(db, entry.reviewId);

  await mirrorModeration(db, entry.reviewId, 'reject');

  // Mark removal request approved
  await setReviewRemovalDecision(db, removalId, 'APPROVED', now);

  // Load review to get vendor ID and review body for counter increment + email
  const [review] = await db
    .select({ vendorId: reviews.vendorId, body: reviews.body })
    .from(reviews)
    .where(eq(reviews.id, entry.reviewId))
    .limit(1);

  if (review?.vendorId) {
    await incrementVendorRemovedReviewsCount(db, review.vendorId);
  }

  await recordAdminAction(db, {
    adminId,
    targetType: 'REVIEW',
    targetId: entry.reviewId,
    action: 'REMOVE_REVIEW',
    note: `Removal request ${removalId} approved`,
  });

  // Send email to vendor (best-effort - do not throw on failure)
  try {
    if (review?.vendorId) {
      const [vendor] = await db
        .select({ email: vendors.email, displayName: vendors.displayName })
        .from(vendors)
        .where(eq(vendors.id, review.vendorId))
        .limit(1);

      if (vendor?.email) {
        await sendReviewRemovalApproved(deps.email, {
          to: vendor.email,
          reviewBody: review.body,
          vendorName: vendor.displayName,
          dashboardUrl: 'https://multideal.co.il/vendor/dashboard',
        });
      }
    }
  } catch (err) {
    captureCaught(err, { scope: 'server.admin.review-removal-queue', severity: 'warning' });
    // Non-fatal: email failure should not roll back the approval
  }
}

// ─── rejectRemoval ────────────────────────────────────────────────────────────

export async function rejectRemoval(
  deps: ReviewRemovalDeps,
  input: { adminId: string; removalId: string },
): Promise<void> {
  const { db } = deps;
  const { adminId, removalId } = input;

  const [entry] = await db
    .select({
      id: reviewRemovalQueue.id,
      reviewId: reviewRemovalQueue.reviewId,
      status: reviewRemovalQueue.status,
    })
    .from(reviewRemovalQueue)
    .where(eq(reviewRemovalQueue.id, removalId))
    .limit(1);

  if (!entry) {
    throw new ReviewRemovalError('NOT_FOUND', 'Removal request not found');
  }
  if (entry.status !== 'PENDING') {
    throw new ReviewRemovalError('INVALID_STATE', `Request is already ${entry.status}`);
  }

  const now = new Date();

  // Update review removal status
  await rejectReviewRemoval(db, entry.reviewId);

  await mirrorModeration(db, entry.reviewId, 'approve');

  // Mark removal request rejected
  await setReviewRemovalDecision(db, removalId, 'REJECTED', now);

  await recordAdminAction(db, {
    adminId,
    targetType: 'REVIEW',
    targetId: entry.reviewId,
    action: 'RESTORE_REVIEW',
    note: `Removal request ${removalId} rejected`,
  });

  // Send email to vendor (best-effort)
  try {
    const [review] = await db
      .select({ vendorId: reviews.vendorId, body: reviews.body })
      .from(reviews)
      .where(eq(reviews.id, entry.reviewId))
      .limit(1);

    if (review?.vendorId) {
      const [vendor] = await db
        .select({ email: vendors.email, displayName: vendors.displayName })
        .from(vendors)
        .where(eq(vendors.id, review.vendorId))
        .limit(1);

      if (vendor?.email) {
        await sendReviewRemovalRejected(deps.email, {
          to: vendor.email,
          reviewBody: review.body,
          vendorName: vendor.displayName,
          dashboardUrl: 'https://multideal.co.il/vendor/dashboard',
        });
      }
    }
  } catch (err) {
    captureCaught(err, { scope: 'server.admin.review-removal-queue', severity: 'warning' });
    // Non-fatal
  }
}

// ─── prescoreViaAi ────────────────────────────────────────────────────────────

export async function prescoreViaAi(
  deps: ReviewRemovalDeps,
  removalId: string,
): Promise<{ shouldRemove: boolean; confidence: number; explanation: string }> {
  const { db } = deps;

  const [entry] = await db
    .select({
      id: reviewRemovalQueue.id,
      reviewId: reviewRemovalQueue.reviewId,
      reason: reviewRemovalQueue.reason,
    })
    .from(reviewRemovalQueue)
    .where(eq(reviewRemovalQueue.id, removalId))
    .limit(1);

  if (!entry) {
    throw new ReviewRemovalError('NOT_FOUND', 'Removal request not found');
  }

  const [review] = await db
    .select({
      body: reviews.body,
      rating: reviews.rating,
      vendorReply: reviews.vendorReply,
    })
    .from(reviews)
    .where(eq(reviews.id, entry.reviewId))
    .limit(1);

  if (!review) {
    throw new ReviewRemovalError('NOT_FOUND', 'Review not found for this removal request');
  }

  const result = await deps.aiReviewRemoval.score({
    reviewBody: review.body,
    reviewRating: review.rating,
    vendorReply: review.vendorReply ?? undefined,
    removalReason: entry.reason,
  });

  // Store AI score (0..1 as numeric string)
  await setReviewRemovalAiScore(db, removalId, result.confidence.toFixed(4));

  return result;
}
