import { ConvexError, v } from 'convex/values';

import type { Doc, Id } from './_generated/dataModel';
import { internalMutation, query } from './_generated/server';
import { getOwnedSpoon, getRequiredUserId } from './model';

const reviewStatus = v.union(
  v.literal('queued'),
  v.literal('running'),
  v.literal('completed'),
  v.literal('failed'),
);

const reviewType = v.union(
  v.literal('upstream_update'),
  v.literal('manual_prompt'),
  v.literal('merge_safety'),
);

const risk = v.union(
  v.literal('unknown'),
  v.literal('low'),
  v.literal('medium'),
  v.literal('high'),
);

const recommendedAction = v.union(
  v.literal('sync'),
  v.literal('open_review_pr'),
  v.literal('manual_review'),
  v.literal('do_not_sync'),
  v.literal('unknown'),
);

export const listForSpoon = query({
  args: { spoonId: v.id('spoons'), limit: v.optional(v.number()) },
  handler: async (ctx, { spoonId, limit }) => {
    const ownerId = await getRequiredUserId(ctx);
    await getOwnedSpoon(ctx, spoonId, ownerId);
    return await ctx.db
      .query('aiReviews')
      .withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
      .order('desc')
      .take(limit ?? 25);
  },
});

export const getLatestForSpoon = query({
  args: { spoonId: v.id('spoons') },
  handler: async (ctx, { spoonId }) => {
    const ownerId = await getRequiredUserId(ctx);
    await getOwnedSpoon(ctx, spoonId, ownerId);
    return await ctx.db
      .query('aiReviews')
      .withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
      .order('desc')
      .first();
  },
});

export const listRecent = query({
  args: { limit: v.optional(v.number()) },
  handler: async (ctx, { limit }) => {
    const ownerId = await getRequiredUserId(ctx);
    return await ctx.db
      .query('aiReviews')
      .withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
      .order('desc')
      .take(limit ?? 25);
  },
});

export const createInternal = internalMutation({
  args: {
    spoonId: v.id('spoons'),
    ownerId: v.id('users'),
    syncRunId: v.optional(v.id('syncRuns')),
    model: v.string(),
    status: reviewStatus,
    reviewType,
    inputSummary: v.string(),
  },
  handler: async (ctx, args): Promise<Id<'aiReviews'>> => {
    const now = Date.now();
    return await ctx.db.insert('aiReviews', {
      ...args,
      risk: 'unknown',
      compatible: false,
      requiresHumanReview: true,
      recommendedAction: 'unknown',
      createdAt: now,
      updatedAt: now,
    });
  },
});

export const completeInternal = internalMutation({
  args: {
    reviewId: v.id('aiReviews'),
    outputSummary: v.string(),
    risk,
    compatible: v.boolean(),
    requiresHumanReview: v.boolean(),
    recommendedAction,
    potentialConflicts: v.array(v.string()),
    importantFiles: v.array(v.string()),
    reasoningSummary: v.string(),
  },
  handler: async (ctx, args) => {
    const review = await ctx.db.get(args.reviewId);
    if (!review) throw new ConvexError('AI review not found.');
    const patch: Partial<Doc<'aiReviews'>> = {
      status: 'completed',
      outputSummary: args.outputSummary,
      risk: args.risk,
      compatible: args.compatible,
      requiresHumanReview: args.requiresHumanReview,
      recommendedAction: args.recommendedAction,
      potentialConflicts: args.potentialConflicts,
      importantFiles: args.importantFiles,
      reasoningSummary: args.reasoningSummary,
      updatedAt: Date.now(),
      completedAt: Date.now(),
    };
    await ctx.db.patch(args.reviewId, patch);
    return review;
  },
});

export const failInternal = internalMutation({
  args: { reviewId: v.id('aiReviews'), error: v.string() },
  handler: async (ctx, { reviewId, error }) => {
    await ctx.db.patch(reviewId, {
      status: 'failed',
      error,
      updatedAt: Date.now(),
    });
    return { success: true };
  },
});
